Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{FqName, SegmentId, SegmentKind, segment_interner};
9use brokk_bifrost_core::analyzer::model::{
10    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
11    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
12    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
13    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
14    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
15};
16use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
17use brokk_bifrost_core::analyzer::structural::materialization::{
18    GenerationKind, MaterializationRecord,
19};
20use brokk_bifrost_core::analyzer::tree_walk::{WalkControl, walk_named_tree_preorder};
21use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
22use brokk_bifrost_core::hash::{HashMap, HashSet};
23use regex::Regex;
24use tree_sitter::{Node, Parser, Tree};
25
26/// Intern one qualified-name segment in the process-global interner.
27fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
28    segment_interner().intern(text, kind)
29}
30
31/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
32/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
33/// `::` head is exactly the mixed-separator store issue #1163 is about; the
34/// structured form records each namespace component, and the equivalence check
35/// renders it natively (with `::` between adjacent Package segments) so it
36/// round-trips to the legacy string. Splitting the already-joined string here is
37/// the M1 bridge — the legacy strings stay authoritative until M3.
38fn cpp_push_package(fq: &mut FqName, package_name: &str) {
39    for component in package_name.split("::").filter(|c| !c.is_empty()) {
40        fq.push(cpp_segment(component, SegmentKind::Package));
41    }
42}
43
44/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
45/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
46/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
47/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
48/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
49/// cpp-specific native rendering rule is needed for this chain.
50fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
51    let mut first = true;
52    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
53    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
54    // entry point, not re-inference of an already-structured name.
55    for component in chain.split('$').filter(|c| !c.is_empty()) {
56        let kind = if first {
57            SegmentKind::Type
58        } else {
59            SegmentKind::Nested
60        };
61        fq.push(cpp_segment(component, kind));
62        first = false;
63    }
64}
65
66/// Structured name for a C++ namespace module: every `::`-separated component is
67/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
68/// `short_name` with an empty `package_name`).
69fn cpp_namespace_fq(full_name: &str) -> FqName {
70    let mut fq = FqName::new();
71    cpp_push_package(&mut fq, full_name);
72    fq
73}
74
75/// The per-level namespace components a `namespace_definition`'s `name` field
76/// declares.
77///
78/// A C++17 nested definition (`namespace a::b::c`) parses as a
79/// `nested_namespace_specifier` whose named children are the per-level
80/// `namespace_identifier`s plus, for three or more levels, a further
81/// `nested_namespace_specifier`; the `::` separators, the optional per-level
82/// `inline`, and the leading global `::` are all anonymous tokens the walk
83/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
84/// segment path as the expanded `namespace a { namespace b { } }` form.
85///
86/// A shape outside that grammar is the deliberately ill-formed source the
87/// diagnostic corpora carry. Those keep their historical single-component
88/// reading of the raw name text, which the caller still joins to the lexical
89/// namespace exactly as before.
90fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
91    let mut components = Vec::new();
92    let mut stack = vec![node];
93    while let Some(current) = stack.pop() {
94        match current.kind() {
95            "namespace_identifier" | "identifier" => {
96                components.push(normalize_cpp_whitespace(node_text(current, source)));
97            }
98            "nested_namespace_specifier" => {
99                for index in (0..current.named_child_count()).rev() {
100                    stack.push(
101                        current
102                            .named_child(index)
103                            .expect("index below the node's own named child count"),
104                    );
105                }
106            }
107            _ => return cpp_raw_namespace_name_components(node, source),
108        }
109    }
110    if components.iter().any(String::is_empty) {
111        return cpp_raw_namespace_name_components(node, source);
112    }
113    components
114}
115
116/// The historical reading of a namespace name node: its whole source text as
117/// one component, with a leading global `::` marker dropped so the caller's
118/// global-scope handling stays the AST boundary rather than a text prefix.
119fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120    let start = node
121        .child(0)
122        .filter(|child| !child.is_named() && child.kind() == "::")
123        .map_or(node.start_byte(), |marker| marker.end_byte());
124    let text = normalize_cpp_whitespace(
125        source
126            .get(start..node.end_byte())
127            .expect("namespace name node covers one source range"),
128    );
129    if text.is_empty() {
130        return Vec::new();
131    }
132    vec![text]
133}
134
135/// Return the named namespace path that structurally encloses `node`.
136///
137/// This intentionally follows namespace AST ancestors rather than inspecting
138/// source text. Anonymous namespaces are not representable in the legacy C++
139/// package field, so a path containing one fails closed.
140fn cpp_lexical_namespace_name(node: Node<'_>, source: &str) -> Option<String> {
141    let mut components = Vec::new();
142    let mut ancestor = node.parent();
143    while let Some(current) = ancestor {
144        if current.kind() == "namespace_definition" {
145            let name_node = current.child_by_field_name("name")?;
146            let name = normalize_cpp_whitespace(node_text(name_node, source));
147            if name.is_empty() {
148                return None;
149            }
150            components.push(name);
151        }
152        ancestor = current.parent();
153    }
154    if components.is_empty() {
155        return None;
156    }
157    components.reverse();
158    Some(components.join("::"))
159}
160
161/// Structured name for a class-like unit: the enclosing namespace's `Package`
162/// segments followed by the `$`-joined nested-class `Type` chain in `short_name`.
163fn cpp_class_fq(package_name: &str, short_name: &str) -> FqName {
164    let mut fq = FqName::new();
165    cpp_push_package(&mut fq, package_name);
166    cpp_push_type_chain(&mut fq, short_name);
167    fq
168}
169
170/// Structured name for a member unit (function, field, enumerator). The
171/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
172/// the member has an owner, by `.member`; free functions and globals have no
173/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
174/// C++ member names never contain a literal `.`, so the single `.` (if any)
175/// separates the owner chain from the member.
176pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
177    let mut fq = FqName::new();
178    cpp_push_package(&mut fq, package_name);
179    match short_name.rsplit_once('.') {
180        Some((owner_chain, member)) => {
181            cpp_push_type_chain(&mut fq, owner_chain);
182            fq.push(cpp_segment(member, SegmentKind::Member));
183        }
184        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
185    }
186    fq
187}
188
189#[derive(Clone)]
190pub struct ScopeInfo {
191    package_name: String,
192    module: Option<CodeUnit>,
193    class_unit: Option<CodeUnit>,
194    template_signature: Option<String>,
195    template_metadata: Option<CppTemplateMetadata>,
196    declarations_are_fields: bool,
197    recovered_specialization_member_scope: bool,
198    /// Namespace targets of every `using namespace X;` directive lexically
199    /// visible at this point in the file (declaration order), threaded
200    /// forward sibling-by-sibling by the sequential container walk (see
201    /// `CppWork::Siblings`). An out-of-line member definition written as a
202    /// bare `Class::method` at file/namespace scope with no enclosing
203    /// `namespace {}` block (issue #1093, e.g. log4cxx's
204    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
205    /// const { ... }`) has no other structural signal for which namespace
206    /// actually owns `Class`; this is the best-effort candidate list used to
207    /// recover it so the definition's indexed identity matches its header
208    /// declaration's.
209    visible_using_namespaces: Vec<String>,
210}
211
212struct CppContainer<'tree> {
213    node: Node<'tree>,
214    scope: ScopeInfo,
215}
216
217struct CppNodeWork<'tree> {
218    node: Node<'tree>,
219    scope: ScopeInfo,
220}
221
222/// Cursor over one container's remaining named children, processed one at a
223/// time (rather than all at once) so a `using namespace X;` sibling can
224/// update `scope.visible_using_namespaces` for the siblings that follow it,
225/// matching real C++ using-directive semantics. Nested container work is
226/// still pushed and fully drained before the cursor resumes (stack LIFO
227/// order), preserving the original left-to-right visitation order.
228struct CppSiblingsWork<'tree> {
229    children: std::vec::IntoIter<Node<'tree>>,
230    scope: ScopeInfo,
231}
232
233enum CppWork<'tree> {
234    Container(CppContainer<'tree>),
235    Node(CppNodeWork<'tree>),
236    Siblings(CppSiblingsWork<'tree>),
237}
238
239fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
240    let best = class_like_name_from_children(node, source);
241    if let Some(parent) = node.parent()
242        && matches!(
243            parent.kind(),
244            "declaration" | "field_declaration" | "function_definition"
245        )
246        && node
247            .child_by_field_name("name")
248            .map(|name_node| {
249                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
250            })
251            .unwrap_or(false)
252        && let Some(recovered) = exported_class_name_from_node(parent, source)
253        && best.as_deref() != Some(recovered.as_str())
254    {
255        return Some(recovered);
256    }
257    best.or_else(|| {
258        node.child_by_field_name("name")
259            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
260            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
261    })
262}
263
264fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
265    let mut grammar_name = None;
266    if let Some(name_node) = node.child_by_field_name("name") {
267        let name = normalize_cpp_whitespace(node_text(name_node, source));
268        if name.is_empty() {
269            return None;
270        }
271        if !cpp_export_macro_token(&name) {
272            return Some(name);
273        }
274        grammar_name = Some(name);
275    }
276
277    let mut best = None;
278    let mut cursor = node.walk();
279    let mut stack = Vec::new();
280    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
281        if matches!(
282            child.kind(),
283            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
284        ) {
285            break;
286        }
287        stack.push(child);
288    }
289
290    while let Some(current) = stack.pop() {
291        if matches!(current.kind(), "type_identifier" | "identifier") {
292            let name = normalize_cpp_whitespace(node_text(current, source));
293            if !name.is_empty() && !cpp_export_macro_token(&name) {
294                best = Some(name);
295            }
296            continue;
297        }
298
299        for index in (0..current.named_child_count()).rev() {
300            if let Some(child) = current.named_child(index) {
301                stack.push(child);
302            }
303        }
304    }
305    best.or(grammar_name)
306}
307
308pub fn cpp_export_macro_token(token: &str) -> bool {
309    token
310        .chars()
311        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
312}
313
314struct RecoveredExportedClass<'tree> {
315    declaration_node: Node<'tree>,
316    name: String,
317    body: Option<Node<'tree>>,
318    raw_supertypes: Option<Vec<String>>,
319    uses_initializer_body: bool,
320    /// Present only for the fragmented multiple-base export shape (issue #938).
321    /// Carries the true class-body byte region -- the members tree-sitter scattered
322    /// out of the recovered node -- so they can be reparsed and re-owned as members
323    /// rather than lost inside the truncated `initializer_list` stand-in.
324    fragmented_body: Option<FragmentedExportBody>,
325}
326
327/// The recovered class-body geometry for a fragmented multiple-base export class.
328/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
329/// verbatim for a region reparse (issue #941 machinery) so every recovered member
330/// keeps its exact original byte/line position. `class_range` is the full class
331/// navigation range spanning to the displaced closing brace.
332struct FragmentedExportBody {
333    reparse_start: usize,
334    reparse_end: usize,
335    class_range: Range,
336}
337
338/// Result of validating a reparsed fragmented class body.  A complete tree can
339/// safely consume the whole region.  A partial tree may contain only the exact
340/// class-named constructor that tree-sitter merged into an access label; its
341/// remaining siblings must stay on the ordinary outer walk.
342enum FragmentedExportMembers {
343    Complete(Tree),
344    ConditionalConstructor(Tree),
345}
346
347#[derive(Clone, Copy)]
348struct DisplacedMacroClassTail {
349    split_index: usize,
350    class_range: Range,
351}
352
353fn recover_exported_class_declaration<'tree>(
354    node: Node<'tree>,
355    source: &str,
356) -> Option<RecoveredExportedClass<'tree>> {
357    if let Some(recovered) = recover_malformed_exported_multiple_base_class(node, source) {
358        return Some(recovered);
359    }
360
361    let class_node = first_class_like_child(node)?;
362    if let Some(name_node) = class_node.child_by_field_name("name") {
363        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
364        if cpp_export_macro_token(&class_name) {
365            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
366            // Name declarator. Only a bare declarator can be the displaced class name;
367            // wrappers describe an object whose type merely happens to look macro-like.
368            let mut cursor = node.walk();
369            if node
370                .children_by_field_name("declarator", &mut cursor)
371                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
372            {
373                return None;
374            }
375        } else if has_direct_cpp_declarator(node) {
376            return None;
377        }
378    }
379    let name = exported_class_name_from_node(class_node, source)?;
380    Some(RecoveredExportedClass {
381        declaration_node: class_node,
382        name,
383        body: cpp_body_node(class_node),
384        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
385            .then(|| extract_cpp_supertypes(class_node, source)),
386        uses_initializer_body: false,
387        fragmented_body: None,
388    })
389}
390
391fn recover_malformed_exported_multiple_base_class<'tree>(
392    node: Node<'tree>,
393    source: &str,
394) -> Option<RecoveredExportedClass<'tree>> {
395    if node.kind() != "declaration" {
396        return None;
397    }
398    let class_node = node.child_by_field_name("type")?;
399    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
400        return None;
401    }
402    let macro_name = class_node
403        .child_by_field_name("name")
404        .and_then(|name| direct_identifier_name(name, source))?;
405    if !cpp_export_macro_token(&macro_name) {
406        return None;
407    }
408
409    let mut named_cursor = node.walk();
410    let mut named = node.named_children(&mut named_cursor);
411    if named
412        .next()
413        .is_none_or(|child| !same_node(child, class_node))
414    {
415        return None;
416    }
417    let displaced = named.next()?;
418    if displaced.kind() != "ERROR" {
419        return None;
420    }
421    let name = displaced_exported_class_name(displaced, source)?;
422
423    let remaining = named.collect::<Vec<_>>();
424    let init = *remaining.last()?;
425    if init.kind() != "init_declarator" {
426        return None;
427    }
428    let final_base = init
429        .child_by_field_name("declarator")
430        .and_then(|base| recovered_malformed_base_name(base, source))?;
431    let body = init.child_by_field_name("value")?;
432    // A complete reduction has a real closing brace here. In Chromium's Widget
433    // declaration, tree-sitter instead emits the same direct `}` slot as a
434    // zero-width missing node where the first body macro truncates the prefix.
435    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
436        return None;
437    }
438
439    let mut declarator_cursor = node.walk();
440    let direct_declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
441    if direct_declarators.count() < 2 {
442        return None;
443    }
444    if remaining[..remaining.len() - 1]
445        .iter()
446        .any(|child| match child.kind() {
447            "qualified_identifier"
448            | "scoped_type_identifier"
449            | "type_identifier"
450            | "identifier" => false,
451            "ERROR" => !is_malformed_inheritance_access(*child, source),
452            _ => true,
453        })
454    {
455        return None;
456    }
457
458    let mut raw_supertypes = Vec::new();
459    for base in &remaining[..remaining.len() - 1] {
460        if base.kind() == "ERROR" {
461            continue;
462        }
463        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
464    }
465    raw_supertypes.push(final_base);
466
467    Some(RecoveredExportedClass {
468        declaration_node: node,
469        name,
470        body: Some(body),
471        raw_supertypes: Some(raw_supertypes),
472        uses_initializer_body: true,
473        fragmented_body: fragmented_export_body_region(node, body, source),
474    })
475}
476
477/// Locate the true class-body region for a fragmented multiple-base export class.
478///
479/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
480/// emits in place of the real class body. Tree-sitter reduces that body in one of
481/// two shapes, both of which lose the members from the recovered node:
482///
483/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
484///   a real closing brace and holds the whole body text inline. The interior between
485///   the braces reparses to the members directly.
486/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
487///   first member with a zero-width MISSING `}`; every later member -- and the real
488///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
489///   siblings. The interior runs from the opening brace to that displaced `}`.
490///
491/// Returns the interior byte range to reparse plus the full class navigation range.
492fn fragmented_export_body_region(
493    node: Node<'_>,
494    body: Node<'_>,
495    source: &str,
496) -> Option<FragmentedExportBody> {
497    let reparse_start = body.start_byte() + 1;
498    let close = direct_close_brace(body)?;
499    if close.end_byte() > close.start_byte() {
500        return Some(FragmentedExportBody {
501            reparse_start,
502            reparse_end: close.start_byte(),
503            class_range: cpp_declaration_range(node),
504        });
505    }
506    // The closing brace was displaced past the recovered node. A balanced nested
507    // class keeps its own braces, so the first lone-`}` sibling is this class's.
508    let mut sibling = node.next_named_sibling();
509    let displaced_close = loop {
510        let Some(current) = sibling else {
511            break displaced_fragment_close_at_namespace_boundary(node, body, source)?;
512        };
513        if cpp_is_stray_close_brace(current, source) {
514            break current;
515        }
516        sibling = current.next_named_sibling();
517    };
518    Some(FragmentedExportBody {
519        reparse_start,
520        reparse_end: displaced_close.start_byte(),
521        class_range: Range {
522            start_byte: node.start_byte(),
523            end_byte: displaced_close.end_byte(),
524            start_line: node.start_position().row + 1,
525            end_line: displaced_close.end_position().row + 1,
526        },
527    })
528}
529
530/// Locate the true class-body region for the export-macro class shape that
531/// tree-sitter promotes to a `function_definition`.
532///
533/// In this shape the synthetic function body closes at the first inline
534/// method, while the class's real members continue as root-level siblings until
535/// a stray `}` followed by the displaced class `;`. Reparse the complete
536/// interior so those siblings are visited with the recovered class scope.
537fn fragmented_export_function_body_region(
538    node: Node<'_>,
539    body: Node<'_>,
540    source: &str,
541) -> Option<FragmentedExportBody> {
542    let reparse_start = body.start_byte().checked_add(1)?;
543    let siblings = cpp_following_named_siblings(node, source);
544    let boundary = fragmented_export_sibling_class_boundary(node, source);
545    let boundary_index = boundary.and_then(|boundary| {
546        siblings
547            .iter()
548            .position(|candidate| same_node(*candidate, boundary))
549    });
550    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
551    let mut sibling_index = 0;
552    // A complete recovered class's synthetic wrapper is immediately followed
553    // by its displaced semicolon (comments may sit between the body and that
554    // semicolon). Only scan for a later stray close when real member siblings
555    // intervene; otherwise every earlier complete class would borrow the next
556    // malformed class's close and claim its members.
557    while let Some(current) = siblings.get(sibling_index).copied() {
558        if current.kind() == "comment" {
559            sibling_index += 1;
560            continue;
561        }
562        if cpp_is_stray_semicolon(current, source) {
563            return None;
564        }
565        break;
566    }
567    while let Some(current) = siblings.get(sibling_index).copied() {
568        let next = siblings.get(sibling_index + 1).copied();
569        if cpp_is_stray_close_brace(current, source)
570            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
571        {
572            let semicolon = next.expect("checked above");
573            return Some(FragmentedExportBody {
574                reparse_start,
575                reparse_end: current.start_byte(),
576                class_range: Range {
577                    start_byte: node.start_byte(),
578                    end_byte: semicolon.end_byte(),
579                    start_line: node.start_position().row + 1,
580                    end_line: semicolon.end_position().row + 1,
581                },
582            });
583        }
584        // When the final access label keeps the class close in its malformed
585        // declaration body, tree-sitter nests the lone `}` ERROR below the
586        // label instead of exposing it as a direct sibling. Search only the
587        // scattered siblings after the synthetic wrapper. The first such
588        // close is the class terminator because nested class bodies retain
589        // their own balanced class_specifier nodes.
590        if current.start_byte() >= body.end_byte()
591            && let Some(close) = cpp_nested_stray_close_brace(current, source)
592        {
593            return Some(FragmentedExportBody {
594                reparse_start,
595                reparse_end: close.start_byte(),
596                class_range: Range {
597                    start_byte: node.start_byte(),
598                    end_byte: current.end_byte(),
599                    start_line: node.start_position().row + 1,
600                    end_line: current.end_position().row + 1,
601                },
602            });
603        }
604        sibling_index += 1;
605    }
606    boundary.map(|boundary| FragmentedExportBody {
607        reparse_start,
608        reparse_end: boundary.start_byte(),
609        class_range: Range {
610            start_byte: node.start_byte(),
611            end_byte: boundary.start_byte(),
612            start_line: node.start_position().row + 1,
613            end_line: boundary.start_position().row + 1,
614        },
615    })
616}
617
618/// Find a later macro-export class that tree-sitter lifted through an enclosing
619/// preprocessor container. A class that is still a direct sibling can be a
620/// nested member of the current fragmented class, so only a changed parent is
621/// a proven boundary between the two recovered class envelopes.
622fn fragmented_export_sibling_class_boundary<'tree>(
623    node: Node<'tree>,
624    source: &str,
625) -> Option<Node<'tree>> {
626    let node_parent = node.parent()?;
627    cpp_following_named_siblings(node, source)
628        .into_iter()
629        .find(|candidate| {
630            recover_exported_class_function_definition(*candidate, source).is_some()
631                && candidate
632                    .parent()
633                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
634        })
635}
636
637/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
638/// export-class wrapper can place the class close inside an access-label node,
639/// so direct-sibling checks alone miss the boundary.  Walk named CST children
640/// only; the helper does not inspect source text beyond the existing structured
641/// stray-brace predicate.
642fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
643    let mut stack = vec![node];
644    while let Some(current) = stack.pop() {
645        if cpp_is_stray_close_brace(current, source) {
646            return Some(current);
647        }
648        let mut cursor = current.walk();
649        stack.extend(current.named_children(&mut cursor));
650    }
651    None
652}
653
654/// Return named siblings that follow `node`, including siblings that tree-sitter
655/// attached to an enclosing container after malformed recovery split the local
656/// declaration list. Stop at the first structurally visible class close so a
657/// later namespace or exported class cannot supply the recovery boundary.
658fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
659    let mut siblings = Vec::new();
660    let mut anchor = node;
661    while let Some(parent) = anchor.parent() {
662        let at_translation_unit = parent.kind() == "translation_unit";
663        let mut sibling = anchor.next_named_sibling();
664        while let Some(current) = sibling {
665            if at_translation_unit
666                && (current.kind() == "namespace_definition"
667                    || (current.kind() == "function_definition"
668                        && first_class_like_child(current).is_some()))
669            {
670                return siblings;
671            }
672            siblings.push(current);
673            if cpp_is_stray_close_brace(current, source) {
674                if let Some(semicolon) = current
675                    .next_named_sibling()
676                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
677                {
678                    siblings.push(semicolon);
679                }
680                return siblings;
681            }
682            if current.start_byte() >= node.end_byte()
683                && matches!(current.kind(), "ERROR" | "labeled_statement")
684                && cpp_nested_stray_close_brace(current, source).is_some()
685            {
686                return siblings;
687            }
688            sibling = current.next_named_sibling();
689        }
690        anchor = parent;
691    }
692    siblings
693}
694
695fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
696    if node.start_byte() >= class_end {
697        return false;
698    }
699    node.end_byte() <= class_end
700        || cpp_nested_stray_close_brace(node, source)
701            .is_some_and(|close| close.start_byte() == class_end)
702}
703
704/// Recover a plain class whose opening prefix is retained in one ERROR node
705/// while one or more nested class closes and the outer close are displaced to
706/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
707/// export-class recovery above. All boundaries come from tree-sitter nodes: the
708/// direct class tokens establish nesting depth and the displaced close nodes
709/// terminate it.
710fn fragmented_plain_class_body(
711    node: Node<'_>,
712    source: &str,
713) -> Option<(String, FragmentedExportBody)> {
714    if node.kind() != "ERROR" {
715        return None;
716    }
717    let mut cursor = node.walk();
718    let children = node.children(&mut cursor).collect::<Vec<_>>();
719    let keyword = children.first()?;
720    if !matches!(keyword.kind(), "class" | "struct" | "union") {
721        return None;
722    }
723    let name_node = children
724        .iter()
725        .copied()
726        .skip(1)
727        .find(|child| child.is_named())?;
728    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
729        return None;
730    }
731    let name = normalize_cpp_whitespace(node_text(name_node, source));
732    if name.is_empty() || cpp_export_macro_token(&name) {
733        return None;
734    }
735    let open_index = children.iter().position(|child| child.kind() == "{")?;
736    let open = children[open_index];
737    let nested_class_opens = children[open_index + 1..]
738        .iter()
739        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
740        .count();
741    let mut closes_remaining = 1 + nested_class_opens;
742    let mut sibling = node.next_named_sibling();
743    while let Some(candidate) = sibling {
744        let next = candidate.next_named_sibling();
745        if cpp_is_stray_close_brace(candidate, source) {
746            closes_remaining -= 1;
747            if closes_remaining == 0 {
748                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
749                if open.end_byte() >= candidate.start_byte() {
750                    return None;
751                }
752                return Some((
753                    name,
754                    FragmentedExportBody {
755                        reparse_start: open.end_byte(),
756                        reparse_end: candidate.start_byte(),
757                        class_range: Range {
758                            start_byte: node.start_byte(),
759                            end_byte: semicolon.end_byte(),
760                            start_line: node.start_position().row + 1,
761                            end_line: semicolon.end_position().row + 1,
762                        },
763                    },
764                ));
765            }
766        }
767        sibling = next;
768    }
769    None
770}
771
772fn displaced_fragment_close_at_namespace_boundary<'tree>(
773    declaration: Node<'tree>,
774    body: Node<'tree>,
775    source: &str,
776) -> Option<Node<'tree>> {
777    let declaration_list = declaration.parent()?;
778    if declaration_list.kind() != "declaration_list" {
779        return None;
780    }
781    let namespace = declaration_list.parent()?;
782    if namespace.kind() != "namespace_definition"
783        || namespace.child_by_field_name("body") != Some(declaration_list)
784    {
785        return None;
786    }
787    let class_close = direct_close_brace(declaration_list)?;
788    let trailing_semicolon = namespace.next_named_sibling()?;
789    if trailing_semicolon.kind() != "expression_statement"
790        || trailing_semicolon.named_child_count() != 0
791    {
792        return None;
793    }
794    let displaced_namespace_close = trailing_semicolon.next_named_sibling()?;
795    if !cpp_is_stray_close_brace(displaced_namespace_close, source) {
796        return None;
797    }
798    let reparse_start = body.start_byte() + 1;
799    let tree = cpp_reparse_region_items(source, reparse_start, class_close.start_byte())?;
800    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(class_close)
801}
802
803/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
804fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
805    (0..node.child_count())
806        .filter_map(|index| node.child(index))
807        .find(|child| !child.is_named() && child.kind() == "}")
808}
809
810/// A displaced lone closing brace: the class close that the fragmented multiple-base
811/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
812fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
813    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
814}
815
816/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
817/// text while skipping line/block comments and string/char literals. The
818/// exported-class recovery needs this when tree-sitter's bogus
819/// `function_definition` body runs past the class's true closing brace and
820/// swallows following siblings (issue #1524): the grammar tree carries no
821/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
822/// close is located textually. Returns `None` when the text is unbalanced or
823/// contains a construct the scanner deliberately does not interpret (raw
824/// strings) -- callers treat that as "cannot partition" and keep the
825/// un-split recovery.
826fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
827    let bytes = source.as_bytes();
828    if bytes.get(open_byte) != Some(&b'{') {
829        return None;
830    }
831    let mut depth = 0usize;
832    let mut i = open_byte;
833    while i < bytes.len() {
834        match bytes[i] {
835            b'{' => depth += 1,
836            b'}' => {
837                depth = depth.checked_sub(1)?;
838                if depth == 0 {
839                    return Some(i);
840                }
841            }
842            b'/' if bytes.get(i + 1) == Some(&b'/') => {
843                while i < bytes.len() && bytes[i] != b'\n' {
844                    i += 1;
845                }
846                continue;
847            }
848            b'/' if bytes.get(i + 1) == Some(&b'*') => {
849                i += 2;
850                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
851                    i += 1;
852                }
853                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
854                continue;
855            }
856            quote @ (b'"' | b'\'') => {
857                // Raw strings (R"(...)") can hold unescaped quotes and braces;
858                // bail out rather than mis-count.
859                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
860                    return None;
861                }
862                i += 1;
863                while i < bytes.len() && bytes[i] != quote {
864                    i += if bytes[i] == b'\\' { 2 } else { 1 };
865                }
866                if i >= bytes.len() {
867                    return None;
868                }
869            }
870            _ => {}
871        }
872        i += 1;
873    }
874    None
875}
876
877fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
878    let mut name = None;
879    let mut colon_count = 0;
880    let mut access_count = 0;
881    for index in 0..node.child_count() {
882        let child = node.child(index)?;
883        match child.kind() {
884            "identifier" | "type_identifier" if child.is_named() => {
885                if name.is_some() {
886                    return None;
887                }
888                let candidate = normalize_cpp_whitespace(node_text(child, source));
889                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
890                    return None;
891                }
892                name = Some(candidate);
893            }
894            ":" if !child.is_named() => colon_count += 1,
895            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
896            _ => return None,
897        }
898    }
899    (colon_count == 1 && access_count == 1)
900        .then_some(name)
901        .flatten()
902}
903
904fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
905    if node.kind() != "ERROR" || node.named_child_count() != 1 {
906        return false;
907    }
908    node.named_child(0)
909        .and_then(|child| direct_identifier_name(child, source))
910        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
911}
912
913fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
914    (0..node.child_count()).any(|index| {
915        node.child(index)
916            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
917    })
918}
919
920fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
921    match node.kind() {
922        "type_identifier" | "identifier" | "namespace_identifier" => {
923            recovered_base_atom(node, source)
924        }
925        "template_type" | "template_function" => node
926            .child_by_field_name("name")
927            .and_then(|name| recovered_malformed_base_name(name, source)),
928        "ERROR" => None,
929        "qualified_identifier" | "scoped_type_identifier" => {
930            let suffix = node
931                .child_by_field_name("name")
932                .and_then(|name| recovered_malformed_base_name(name, source))?;
933            let scope = node
934                .child_by_field_name("scope")
935                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
936            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
937                malformed_qualified_prefix(node, source)?
938            } else {
939                if malformed_qualified_prefix(node, source).is_some() {
940                    return None;
941                }
942                scope
943            };
944            Some(format!("{prefix}::{suffix}"))
945        }
946        _ => None,
947    }
948}
949
950fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
951    if !matches!(
952        node.kind(),
953        "identifier" | "type_identifier" | "namespace_identifier"
954    ) {
955        return None;
956    }
957    let name = normalize_cpp_whitespace(node_text(node, source));
958    (!name.is_empty()).then_some(name)
959}
960
961fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
962    let mut prefix = None;
963    let mut cursor = node.walk();
964    for error in node
965        .named_children(&mut cursor)
966        .filter(|child| child.kind() == "ERROR")
967    {
968        if error.named_child_count() != 1 || prefix.is_some() {
969            return None;
970        }
971        prefix = error
972            .named_child(0)
973            .and_then(|child| recovered_base_atom(child, source));
974        prefix.as_ref()?;
975    }
976    prefix
977}
978
979fn recover_exported_class_function_definition<'tree>(
980    node: Node<'tree>,
981    source: &str,
982) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
983    if node.kind() != "function_definition" {
984        return None;
985    }
986    let type_node = node.child_by_field_name("type")?;
987    let declarator = node.child_by_field_name("declarator")?;
988
989    if matches!(
990        type_node.kind(),
991        "class_specifier" | "struct_specifier" | "union_specifier"
992    ) {
993        let type_name = type_node
994            .child_by_field_name("name")
995            .and_then(|name| direct_identifier_name(name, source));
996        let exported_macro_type = type_name
997            .as_ref()
998            .is_some_and(|name| cpp_export_macro_token(name));
999        if exported_macro_type {
1000            let mut cursor = node.walk();
1001            let errors_before_declarator = node
1002                .named_children(&mut cursor)
1003                .filter(|child| {
1004                    child.kind() == "ERROR"
1005                        && child.start_byte() >= type_node.end_byte()
1006                        && child.end_byte() <= declarator.start_byte()
1007                })
1008                .collect::<Vec<_>>();
1009            if let Some(name) = errors_before_declarator
1010                .iter()
1011                .find_map(|error| displaced_exported_class_name(*error, source))
1012            {
1013                let raw_supertypes = errors_before_declarator
1014                    .iter()
1015                    .any(|error| malformed_inheritance_syntax(*error))
1016                    .then(|| recovered_malformed_base_name(declarator, source))
1017                    .flatten()
1018                    .map(|base| vec![base]);
1019                return Some((node, name, raw_supertypes));
1020            }
1021            if errors_before_declarator
1022                .iter()
1023                .any(|error| malformed_inheritance_syntax(*error))
1024            {
1025                return None;
1026            }
1027        }
1028        if !exported_macro_type
1029            && let Some(name) = type_name
1030            && !cpp_export_macro_token(&name)
1031            && let Some(base) =
1032                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1033        {
1034            return Some((node, name, Some(vec![base])));
1035        }
1036        if let Some(name) = direct_identifier_name(declarator, source)
1037            && exported_macro_type
1038            && !cpp_export_macro_token(&name)
1039        {
1040            let raw_supertypes = exported_macro_type
1041                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1042                .flatten()
1043                .map(|base| vec![base]);
1044            return Some((node, name, raw_supertypes));
1045        }
1046        if declarator.kind() == "parenthesized_declarator"
1047            && type_node
1048                .child_by_field_name("name")
1049                .and_then(|name| direct_identifier_name(name, source))
1050                .is_some_and(|name| cpp_export_macro_token(&name))
1051        {
1052            let body_start = node
1053                .child_by_field_name("body")
1054                .map(|body| body.start_byte())
1055                .unwrap_or(node.end_byte());
1056            let mut cursor = node.walk();
1057            if let Some(name) = node
1058                .named_children(&mut cursor)
1059                .filter(|child| {
1060                    child.kind() == "ERROR"
1061                        && child.start_byte() >= declarator.end_byte()
1062                        && child.end_byte() <= body_start
1063                })
1064                .find_map(|error| declarator_name_from_node(error, source))
1065            {
1066                return Some((node, name, None));
1067            }
1068        }
1069    }
1070
1071    let declarator_text = direct_identifier_name(declarator, source)?;
1072    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1073        return None;
1074    }
1075    class_identifier_before_body(node, source).map(|name| (node, name, None))
1076}
1077
1078/// Recover the class item from a region reparse that still carries the
1079/// sentinel's synthetic function envelope.  An unknown class attribute can
1080/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1081/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1082/// then nested below that function, so direct class-child lookup is not enough.
1083struct CppSentinelReparsedClass<'tree> {
1084    declaration_node: Node<'tree>,
1085    name: String,
1086    body: Node<'tree>,
1087    raw_supertypes: Option<Vec<String>>,
1088}
1089
1090fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1091    let mut cursor = root.walk();
1092    root.named_children(&mut cursor)
1093        .find(|child| child.kind() != "comment")
1094        .filter(|child| child.kind() == "template_declaration")
1095}
1096
1097fn cpp_sentinel_reparsed_class<'tree>(
1098    root: Node<'tree>,
1099    template_node: Option<Node<'tree>>,
1100    source: &str,
1101) -> Option<CppSentinelReparsedClass<'tree>> {
1102    let container = template_node.unwrap_or(root);
1103    let mut cursor = container.walk();
1104    for child in container.named_children(&mut cursor) {
1105        if matches!(
1106            child.kind(),
1107            "class_specifier" | "struct_specifier" | "union_specifier"
1108        ) {
1109            let name = class_like_name(child, source)?;
1110            let body = cpp_body_node(child)?;
1111            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1112                .then(|| extract_cpp_supertypes(child, source));
1113            return Some(CppSentinelReparsedClass {
1114                declaration_node: child,
1115                name,
1116                body,
1117                raw_supertypes,
1118            });
1119        }
1120        if child.kind() == "declaration"
1121            && let Some(class_node) = first_class_like_child(child)
1122        {
1123            let name = class_like_name(class_node, source)?;
1124            let body = cpp_body_node(class_node)?;
1125            let raw_supertypes =
1126                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1127                    .then(|| extract_cpp_supertypes(class_node, source));
1128            return Some(CppSentinelReparsedClass {
1129                declaration_node: class_node,
1130                name,
1131                body,
1132                raw_supertypes,
1133            });
1134        }
1135        // Only when the nested class item carries its own body. A bodyless
1136        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1137        // a function definition -- is the export-macro shape recovered by the
1138        // next arm, and must fall through to it rather than abort the search.
1139        if child.kind() == "function_definition"
1140            && let Some(class_node) = first_class_like_child(child)
1141            && let Some(body) = cpp_body_node(class_node)
1142            && let Some(name) = class_like_name(class_node, source)
1143        {
1144            let raw_supertypes =
1145                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1146                    .then(|| extract_cpp_supertypes(class_node, source));
1147            return Some(CppSentinelReparsedClass {
1148                declaration_node: class_node,
1149                name,
1150                body,
1151                raw_supertypes,
1152            });
1153        }
1154        if child.kind() == "function_definition"
1155            && let Some((_, name, raw_supertypes)) =
1156                recover_exported_class_function_definition(child, source)
1157        {
1158            let body = cpp_body_node(child)?;
1159            return Some(CppSentinelReparsedClass {
1160                declaration_node: child,
1161                name,
1162                body,
1163                raw_supertypes,
1164            });
1165        }
1166    }
1167    None
1168}
1169
1170fn recovered_postfix_export_macro_base(
1171    node: Node<'_>,
1172    type_node: Node<'_>,
1173    declarator: Node<'_>,
1174    source: &str,
1175) -> Option<String> {
1176    let mut cursor = node.walk();
1177    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1178        child.kind() == "ERROR"
1179            && child.start_byte() >= type_node.end_byte()
1180            && child.end_byte() <= declarator.start_byte()
1181            && postfix_export_macro_inheritance(*child, source)
1182    });
1183    malformed_clauses.next()?;
1184    if malformed_clauses.next().is_some() {
1185        return None;
1186    }
1187    recovered_malformed_base_name(declarator, source)
1188}
1189
1190fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1191    let mut macro_count = 0;
1192    let mut colon_count = 0;
1193    let mut access_count = 0;
1194    for index in 0..node.child_count() {
1195        let Some(child) = node.child(index) else {
1196            return false;
1197        };
1198        match child.kind() {
1199            "identifier" | "type_identifier" if child.is_named() => {
1200                let candidate = normalize_cpp_whitespace(node_text(child, source));
1201                if !cpp_export_macro_token(&candidate) {
1202                    return false;
1203                }
1204                macro_count += 1;
1205            }
1206            ":" if !child.is_named() => colon_count += 1,
1207            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1208            _ => return false,
1209        }
1210    }
1211    macro_count == 1 && colon_count == 1 && access_count == 1
1212}
1213
1214fn recovered_single_base_after_declarator(
1215    node: Node<'_>,
1216    declarator: Node<'_>,
1217    source: &str,
1218) -> Option<String> {
1219    let body_start = node
1220        .child_by_field_name("body")
1221        .map(|body| body.start_byte())
1222        .unwrap_or(node.end_byte());
1223    let mut cursor = node.walk();
1224    let mut bases = node
1225        .named_children(&mut cursor)
1226        .filter(|child| {
1227            child.kind() == "ERROR"
1228                && child.start_byte() >= declarator.end_byte()
1229                && child.end_byte() <= body_start
1230        })
1231        .filter_map(|error| displaced_exported_class_name(error, source));
1232    let base = bases.next()?;
1233    bases.next().is_none().then_some(base)
1234}
1235
1236fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1237    (0..node.child_count()).any(|index| {
1238        node.child(index)
1239            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1240    })
1241}
1242
1243pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1244    recover_exported_class_function_definition(node, source).is_some()
1245}
1246
1247fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1248    matches!(
1249        kind,
1250        "ERROR"
1251            | "preproc_if"
1252            | "preproc_ifdef"
1253            | "preproc_ifndef"
1254            | "preproc_else"
1255            | "preproc_elif"
1256    ) || (kind == "labeled_statement" && in_class_scope)
1257}
1258
1259pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1260    if node.kind() != "declaration" {
1261        return false;
1262    }
1263    let mut ancestor = node.parent();
1264    while let Some(container) = ancestor {
1265        match container.kind() {
1266            "compound_statement" => {
1267                return container.parent().is_some_and(|class_container| {
1268                    is_recovered_exported_class_container(class_container, source)
1269                });
1270            }
1271            // These containers preserve ScopeInfo in visit_node. declaration_list is
1272            // the body container selected for a linkage specification.
1273            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1274            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1275            _ => return false,
1276        }
1277        ancestor = container.parent();
1278    }
1279    false
1280}
1281
1282pub fn recovered_exported_class_has_body(
1283    node: Node<'_>,
1284    source: &str,
1285    expected_name: &str,
1286) -> Option<bool> {
1287    match node.kind() {
1288        "function_definition" => {
1289            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1290            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1291        }
1292        "declaration" | "field_declaration" => {
1293            let recovered = recover_exported_class_declaration(node, source)?;
1294            (recovered.name == expected_name).then(|| recovered.body.is_some())
1295        }
1296        _ => None,
1297    }
1298}
1299
1300fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1301    let body_start = node
1302        .child_by_field_name("body")
1303        .map(|body| body.start_byte())
1304        .unwrap_or(node.end_byte());
1305    let mut stack = Vec::new();
1306    for index in (0..node.named_child_count()).rev() {
1307        let Some(child) = node.named_child(index) else {
1308            continue;
1309        };
1310        if child.start_byte() >= body_start {
1311            continue;
1312        }
1313        stack.push(child);
1314    }
1315
1316    let mut best = None;
1317    while let Some(current) = stack.pop() {
1318        if matches!(current.kind(), "identifier" | "type_identifier") {
1319            let name = normalize_cpp_whitespace(node_text(current, source));
1320            if !name.is_empty()
1321                && !cpp_export_macro_token(&name)
1322                && !matches!(name.as_str(), "class" | "struct" | "union")
1323            {
1324                best = Some(name);
1325            }
1326            continue;
1327        }
1328
1329        for index in (0..current.named_child_count()).rev() {
1330            if let Some(child) = current.named_child(index)
1331                && child.start_byte() < body_start
1332            {
1333                stack.push(child);
1334            }
1335        }
1336    }
1337    best
1338}
1339
1340fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1341    if node.kind() == "declaration"
1342        && node
1343            .child_by_field_name("type")
1344            .or_else(|| first_class_like_child(node))
1345            .is_some_and(|type_node| {
1346                matches!(
1347                    type_node.kind(),
1348                    "class_specifier" | "struct_specifier" | "union_specifier"
1349                )
1350            })
1351        && let Some(name) = node
1352            .child_by_field_name("declarator")
1353            .and_then(|declarator| declarator_name_from_node(declarator, source))
1354        && !cpp_export_macro_token(&name)
1355    {
1356        return Some(name);
1357    }
1358
1359    if node.kind() == "function_definition"
1360        && node.child_by_field_name("type").is_some_and(|type_node| {
1361            matches!(
1362                type_node.kind(),
1363                "class_specifier" | "struct_specifier" | "union_specifier"
1364            )
1365        })
1366        && let Some(name) = node
1367            .child_by_field_name("declarator")
1368            .and_then(|declarator| direct_identifier_name(declarator, source))
1369        && !cpp_export_macro_token(&name)
1370    {
1371        return Some(name);
1372    }
1373
1374    let class_node = if matches!(
1375        node.kind(),
1376        "class_specifier" | "struct_specifier" | "union_specifier"
1377    ) {
1378        node
1379    } else {
1380        first_class_like_child(node)?
1381    };
1382    class_like_name_from_children(class_node, source)
1383}
1384
1385fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1386    if !matches!(
1387        node.kind(),
1388        "identifier" | "field_identifier" | "type_identifier"
1389    ) {
1390        return None;
1391    }
1392    let name = normalize_cpp_whitespace(node_text(node, source));
1393    (!name.is_empty()).then_some(name)
1394}
1395
1396fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1397    match node.kind() {
1398        "identifier" | "field_identifier" | "type_identifier" => {
1399            let name = normalize_cpp_whitespace(node_text(node, source));
1400            (!name.is_empty()).then_some(name)
1401        }
1402        _ => {
1403            let mut cursor = node.walk();
1404            node.named_children(&mut cursor)
1405                .find_map(|child| declarator_name_from_node(child, source))
1406        }
1407    }
1408}
1409
1410fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1411    let mut cursor = node.walk();
1412    node.named_children(&mut cursor).find(|child| {
1413        matches!(
1414            child.kind(),
1415            "class_specifier" | "struct_specifier" | "union_specifier"
1416        )
1417    })
1418}
1419
1420/// Push a container's children as a `Siblings` cursor rather than snapshotting
1421/// them all with one shared scope: children are visited one at a time so a
1422/// `using namespace X;` sibling can affect the scope threaded to the siblings
1423/// that textually follow it (issue #1093).
1424fn push_cpp_container_work<'tree>(
1425    node: Node<'tree>,
1426    scope: ScopeInfo,
1427    stack: &mut Vec<CppWork<'tree>>,
1428) {
1429    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1430}
1431
1432/// Materialize one selected named-child range with a tree-sitter cursor. The
1433/// cursor advances linearly across the parent's concrete children; repeatedly
1434/// asking for `named_child(index)` is quadratic on very wide generated nodes.
1435fn push_cpp_sibling_range<'tree>(
1436    parent: Node<'tree>,
1437    start_index: usize,
1438    end_index: usize,
1439    scope: ScopeInfo,
1440    stack: &mut Vec<CppWork<'tree>>,
1441) {
1442    let mut cursor = parent.walk();
1443    let children = parent
1444        .named_children(&mut cursor)
1445        .skip(start_index)
1446        .take(end_index.saturating_sub(start_index))
1447        .collect::<Vec<_>>()
1448        .into_iter();
1449    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1450}
1451
1452/// Advance a `Siblings` cursor by one child: dispatch the current child under
1453/// the scope accumulated from its *earlier* siblings, then push a
1454/// continuation for the remaining siblings carrying the scope updated for
1455/// *this* child (only `using namespace X;` directives change it). Pushing the
1456/// continuation before the current child's own node work means the current
1457/// child's subtree fully drains (LIFO) before the next sibling is visited,
1458/// preserving left-to-right order.
1459fn advance_cpp_siblings<'tree>(
1460    mut siblings: CppSiblingsWork<'tree>,
1461    source: &str,
1462    stack: &mut Vec<CppWork<'tree>>,
1463) {
1464    let Some(child) = siblings.children.next() else {
1465        return;
1466    };
1467    let current_scope = siblings.scope.clone();
1468    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1469        siblings.scope.visible_using_namespaces.push(namespace);
1470    }
1471    if !siblings.children.as_slice().is_empty() {
1472        stack.push(CppWork::Siblings(siblings));
1473    }
1474    stack.push(CppWork::Node(CppNodeWork {
1475        node: child,
1476        scope: current_scope,
1477    }));
1478}
1479
1480/// The namespace target of a `using namespace X;` directive, or `None` for
1481/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1482/// kind. Distinguished structurally by the presence of the grammar's literal
1483/// `namespace` keyword token among the node's children -- not by inspecting
1484/// source text -- so it never misreads a member-importing using-declaration
1485/// as a namespace directive.
1486fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1487    if node.kind() != "using_declaration" {
1488        return None;
1489    }
1490    let mut cursor = node.walk();
1491    let is_namespace_directive = node
1492        .children(&mut cursor)
1493        .any(|child| child.kind() == "namespace");
1494    if !is_namespace_directive {
1495        return None;
1496    }
1497    let target = node.named_child(0)?;
1498    let text = normalize_cpp_whitespace(node_text(target, source));
1499    (!text.is_empty()).then_some(text)
1500}
1501
1502/// Every `using namespace X;` directive target in a file, in source order, for
1503/// resolution-time consumers that need the file's using-directives without the
1504/// per-position scope threading extraction does. Parses `source` fresh and
1505/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1506/// keys on the grammar's `namespace` keyword token, not source text), so it
1507/// never misreads a member-importing `using X::Y;` as a namespace directive.
1508///
1509/// This is a whole-file over-approximation of what is in scope at any one point
1510/// (a directive nested inside a `namespace {}` block or a function body is still
1511/// reported), which is exactly what the #1134 identity reconciler wants: extra
1512/// candidate namespaces that no visible class confirms are harmless, and two
1513/// that both confirm are treated as a genuine ambiguity by the reconciler.
1514pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1515    let mut parser = Parser::new();
1516    if parser
1517        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1518        .is_err()
1519    {
1520        return Vec::new();
1521    }
1522    let Some(tree) = parser.parse(source, None) else {
1523        return Vec::new();
1524    };
1525    let mut namespaces = Vec::new();
1526    let mut seen = std::collections::HashSet::new();
1527    let mut stack = vec![tree.root_node()];
1528    while let Some(node) = stack.pop() {
1529        if let Some(namespace) = cpp_using_namespace_target(node, source)
1530            && seen.insert(namespace.clone())
1531        {
1532            namespaces.push(namespace);
1533        }
1534        let mut cursor = node.walk();
1535        stack.extend(node.named_children(&mut cursor));
1536    }
1537    namespaces
1538}
1539
1540pub struct CppVisitor<'a> {
1541    pub file: &'a ProjectFile,
1542    pub source: &'a str,
1543    pub parsed: &'a mut ParsedFile,
1544    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1545    /// Byte regions whose contents were re-owned by a fragmented export-class
1546    /// recovery (#938): the scattered members between the fragmented
1547    /// declaration and its displaced closing brace are indexed as members of
1548    /// the recovered class by the region reparse, so the ordinary sibling walk
1549    /// must not ALSO index them as top-level declarations (that double-indexing
1550    /// made a scattered nested class ambiguous between `Inner` and
1551    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
1552    /// linear scan at visit time is fine.
1553    pub consumed_fragment_regions: Vec<(usize, usize)>,
1554}
1555
1556impl<'a> CppVisitor<'a> {
1557    #[allow(clippy::too_many_arguments)]
1558    pub fn visit_container(
1559        &mut self,
1560        node: Node<'_>,
1561        package_name: &str,
1562        module: Option<CodeUnit>,
1563        class_unit: Option<CodeUnit>,
1564        template_signature: Option<String>,
1565        visible_using_namespaces: Vec<String>,
1566    ) {
1567        let scope = ScopeInfo {
1568            package_name: package_name.to_string(),
1569            module,
1570            class_unit,
1571            template_signature,
1572            template_metadata: None,
1573            declarations_are_fields: false,
1574            recovered_specialization_member_scope: false,
1575            visible_using_namespaces,
1576        };
1577        self.run_container_work(node, scope);
1578    }
1579
1580    /// Whether a work node lies entirely inside a byte region consumed by a
1581    /// fragmented export-class recovery (#938); such nodes were already indexed
1582    /// as members of the recovered class by the region reparse.
1583    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
1584        self.consumed_fragment_regions
1585            .iter()
1586            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
1587    }
1588
1589    /// Drive the container work loop from an explicit seed scope to completion. The
1590    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
1591    /// stays alive for the whole traversal.
1592    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
1593        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
1594        while let Some(work) = stack.pop() {
1595            match work {
1596                CppWork::Container(container) => {
1597                    push_cpp_container_work(container.node, container.scope, &mut stack);
1598                }
1599                CppWork::Siblings(siblings) => {
1600                    advance_cpp_siblings(siblings, self.source, &mut stack);
1601                }
1602                CppWork::Node(work) => {
1603                    if self.node_is_inside_consumed_fragment(work.node) {
1604                        continue;
1605                    }
1606                    self.visit_node(work.node, &work.scope, &mut stack);
1607                }
1608            }
1609        }
1610    }
1611
1612    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
1613    /// it only when the entire region is member-shaped. This validation must happen
1614    /// before registering the recovered class because a rejected speculative range
1615    /// must not leak into the ordinary recovery path.
1616    fn reparse_fragmented_export_class_members(
1617        &self,
1618        fragmented: &FragmentedExportBody,
1619        class_name: &str,
1620    ) -> Option<FragmentedExportMembers> {
1621        if fragmented.reparse_start >= fragmented.reparse_end {
1622            return None;
1623        }
1624        let tree = cpp_reparse_fragmented_class_body(
1625            self.source,
1626            fragmented.reparse_start,
1627            fragmented.reparse_end,
1628        )?;
1629        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
1630            return Some(FragmentedExportMembers::Complete(tree));
1631        }
1632        let has_conditional_constructor = {
1633            let root = tree.root_node();
1634            let mut cursor = root.walk();
1635            root.named_children(&mut cursor).any(|child| {
1636                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
1637            })
1638        };
1639        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
1640    }
1641
1642    /// Index an already validated fragmented body as members of `class_unit`. The
1643    /// region reparse keeps each member's exact original byte and line positions.
1644    fn visit_fragmented_export_class_members(
1645        &mut self,
1646        outcome: FragmentedExportMembers,
1647        class_unit: CodeUnit,
1648        scope: &ScopeInfo,
1649    ) -> bool {
1650        let (tree, complete) = match outcome {
1651            FragmentedExportMembers::Complete(tree) => (tree, true),
1652            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
1653        };
1654        let root = tree.root_node();
1655        let class_name = class_unit.identifier().to_string();
1656        let member_scope = ScopeInfo {
1657            // A recovered export-macro class may borrow its namespace from an
1658            // earlier forward declaration even when the malformed node itself
1659            // sits at file scope. Use the recovered class identity as the
1660            // authoritative package for reparsed members as well.
1661            package_name: class_unit.package_name().to_string(),
1662            module: scope.module.clone(),
1663            class_unit: Some(class_unit),
1664            template_signature: scope.template_signature.clone(),
1665            template_metadata: None,
1666            declarations_are_fields: true,
1667            recovered_specialization_member_scope: false,
1668            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1669        };
1670        if !complete {
1671            // A conditional beginning immediately after an access label can
1672            // fragment one constructor declaration while leaving the rest of
1673            // the class body as unsafe statement soup. Recover only that
1674            // structurally proven constructor and leave the outer-tree
1675            // siblings unconsumed for their ordinary walk.
1676            let mut cursor = root.walk();
1677            let constructors = root
1678                .named_children(&mut cursor)
1679                .filter_map(|child| {
1680                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
1681                })
1682                .collect::<Vec<_>>();
1683            for constructor in constructors {
1684                let mut stack = Vec::new();
1685                self.visit_node(constructor, &member_scope, &mut stack);
1686                while let Some(work) = stack.pop() {
1687                    match work {
1688                        CppWork::Container(container) => {
1689                            push_cpp_container_work(container.node, container.scope, &mut stack);
1690                        }
1691                        CppWork::Siblings(siblings) => {
1692                            advance_cpp_siblings(siblings, self.source, &mut stack);
1693                        }
1694                        CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
1695                    }
1696                }
1697            }
1698            return false;
1699        }
1700        self.run_container_work(root, member_scope);
1701        true
1702    }
1703
1704    fn visit_recovered_fragment_constructor(
1705        &mut self,
1706        range: std::ops::Range<usize>,
1707        constructor_body: Node<'_>,
1708        class_declaration: Node<'_>,
1709        class_unit: &CodeUnit,
1710        scope: &ScopeInfo,
1711    ) {
1712        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
1713            return;
1714        };
1715        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
1716            tree.root_node(),
1717            range.start,
1718            class_unit.identifier(),
1719            self.source,
1720        ) else {
1721            return;
1722        };
1723        let member_scope = ScopeInfo {
1724            package_name: class_unit.package_name().to_string(),
1725            module: scope.module.clone(),
1726            class_unit: Some(class_unit.clone()),
1727            template_signature: scope.template_signature.clone(),
1728            template_metadata: None,
1729            declarations_are_fields: true,
1730            recovered_specialization_member_scope: false,
1731            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1732        };
1733        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
1734        else {
1735            return;
1736        };
1737        debug_assert_eq!(function.name, class_unit.identifier());
1738        let code_unit = function.code_unit(self.file.clone());
1739        self.parsed.add_code_unit_with_range(
1740            code_unit.clone(),
1741            Range {
1742                start_byte: function_declarator.start_byte(),
1743                end_byte: constructor_body.end_byte(),
1744                start_line: function_declarator.start_position().row + 1,
1745                end_line: constructor_body.end_position().row + 1,
1746            },
1747            None,
1748            None,
1749        );
1750        self.parsed.add_signature_with_metadata(
1751            code_unit.clone(),
1752            cpp_signature_metadata(
1753                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
1754                function_declarator,
1755                self.source,
1756            )
1757            .with_declaration_only(false)
1758            .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
1759        );
1760        self.parsed.add_child(class_unit.clone(), code_unit);
1761    }
1762
1763    fn visit_recovered_fragment_prefix_members(
1764        &mut self,
1765        root: Node<'_>,
1766        constructor_start: usize,
1767        class_unit: &CodeUnit,
1768        scope: &ScopeInfo,
1769    ) {
1770        let member_scope = ScopeInfo {
1771            package_name: class_unit.package_name().to_string(),
1772            module: scope.module.clone(),
1773            class_unit: Some(class_unit.clone()),
1774            template_signature: scope.template_signature.clone(),
1775            template_metadata: None,
1776            declarations_are_fields: true,
1777            recovered_specialization_member_scope: false,
1778            visible_using_namespaces: scope.visible_using_namespaces.clone(),
1779        };
1780        let mut stack = vec![root];
1781        while let Some(current) = stack.pop() {
1782            if current.kind() == "comment" || current.start_byte() >= constructor_start {
1783                continue;
1784            }
1785            if current.end_byte() <= constructor_start
1786                && current.kind() != "translation_unit"
1787                && current.kind() != "labeled_statement"
1788                && current.kind() != "ERROR"
1789            {
1790                let mut work_stack = Vec::new();
1791                self.visit_node(current, &member_scope, &mut work_stack);
1792                while let Some(work) = work_stack.pop() {
1793                    match work {
1794                        CppWork::Container(container) => {
1795                            push_cpp_container_work(
1796                                container.node,
1797                                container.scope,
1798                                &mut work_stack,
1799                            );
1800                        }
1801                        CppWork::Siblings(siblings) => {
1802                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
1803                        }
1804                        CppWork::Node(work) => {
1805                            self.visit_node(work.node, &work.scope, &mut work_stack)
1806                        }
1807                    }
1808                }
1809                continue;
1810            }
1811            if matches!(
1812                current.kind(),
1813                "translation_unit" | "labeled_statement" | "ERROR"
1814            ) {
1815                let mut cursor = current.walk();
1816                stack.extend(current.named_children(&mut cursor));
1817            }
1818        }
1819    }
1820
1821    fn visit_node<'tree>(
1822        &mut self,
1823        node: Node<'tree>,
1824        scope: &ScopeInfo,
1825        stack: &mut Vec<CppWork<'tree>>,
1826    ) {
1827        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
1828            self.visit_node(node, &recovered_scope, stack);
1829            return;
1830        }
1831        if let Some((name, fragmented)) = fragmented_plain_class_body(node, self.source) {
1832            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
1833            let mut class_stack = Vec::new();
1834            let class_unit = self.visit_named_class_like_shape(
1835                node,
1836                name,
1837                None,
1838                true,
1839                Some(fragmented.class_range),
1840                Some(extract_cpp_supertypes(node, self.source)),
1841                scope,
1842                &mut class_stack,
1843            );
1844            let member_scope = ScopeInfo {
1845                package_name: class_unit.package_name().to_string(),
1846                module: scope.module.clone(),
1847                class_unit: Some(class_unit.clone()),
1848                template_signature: scope.template_signature.clone(),
1849                template_metadata: None,
1850                declarations_are_fields: true,
1851                recovered_specialization_member_scope: false,
1852                visible_using_namespaces: scope.visible_using_namespaces.clone(),
1853            };
1854            let complete = outcome.is_some_and(|outcome| {
1855                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
1856            });
1857            if complete {
1858                self.consumed_fragment_regions
1859                    .push((node.start_byte(), fragmented.class_range.end_byte));
1860            } else {
1861                // A macro-constrained member can make the full body reparse
1862                // unsafe while tree-sitter still exposes later class members
1863                // as bounded siblings up to the displaced `}`/`;`. Keep the
1864                // structurally proven class/base declaration and re-own those
1865                // sibling nodes under it. They retain their original parser
1866                // nodes and exact ranges; the close boundary comes solely from
1867                // `fragmented_plain_class_body`.
1868                let mut sibling = node.next_named_sibling();
1869                while let Some(candidate) = sibling {
1870                    if candidate.start_byte() >= fragmented.reparse_end {
1871                        break;
1872                    }
1873                    if cpp_fragment_sibling_is_class_member(
1874                        candidate,
1875                        fragmented.reparse_end,
1876                        self.source,
1877                    ) {
1878                        self.recovered_class_sibling_scopes
1879                            .insert(candidate.id(), member_scope.clone());
1880                    }
1881                    sibling = candidate.next_named_sibling();
1882                }
1883            }
1884            stack.extend(class_stack);
1885            return;
1886        }
1887        match node.kind() {
1888            "template_declaration" => {
1889                if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
1890                    let mut template_scope = scope.clone();
1891                    template_scope.template_signature =
1892                        cpp_template_signature(node, recovered.declaration_node, self.source);
1893                    template_scope.template_metadata =
1894                        cpp_template_metadata(node, recovered.class_node, self.source);
1895                    let raw_supertypes =
1896                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
1897                    let mut class_stack = Vec::new();
1898                    let class_unit = self.visit_named_class_like_shape(
1899                        recovered.class_node,
1900                        recovered.name,
1901                        Some(recovered.body),
1902                        true,
1903                        Some(recovered.range),
1904                        raw_supertypes,
1905                        &template_scope,
1906                        &mut class_stack,
1907                    );
1908                    self.parsed.record_materialization(
1909                        MaterializationRecord::RecoveredDeclaration {
1910                            recovery: recovered.range,
1911                            unit: class_unit.clone(),
1912                        },
1913                    );
1914                    let member_scope = ScopeInfo {
1915                        package_name: template_scope.package_name.clone(),
1916                        module: template_scope.module.clone(),
1917                        class_unit: Some(class_unit.clone()),
1918                        template_signature: template_scope.template_signature.clone(),
1919                        template_metadata: None,
1920                        declarations_are_fields: true,
1921                        recovered_specialization_member_scope: recovered
1922                            .class_node
1923                            .child_by_field_name("name")
1924                            .is_some_and(|name| name.kind() == "template_type"),
1925                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
1926                    };
1927                    for tail_member in recovered.tail_members.into_iter().rev() {
1928                        stack.push(CppWork::Node(CppNodeWork {
1929                            node: tail_member,
1930                            scope: member_scope.clone(),
1931                        }));
1932                    }
1933                    stack.extend(class_stack);
1934                    for sibling in recovered.member_siblings {
1935                        self.recovered_class_sibling_scopes
1936                            .insert(sibling.id(), member_scope.clone());
1937                    }
1938                    return;
1939                }
1940                for index in (0..node.named_child_count()).rev() {
1941                    let Some(child) = node.named_child(index) else {
1942                        continue;
1943                    };
1944                    if matches!(
1945                        child.kind(),
1946                        "class_specifier"
1947                            | "struct_specifier"
1948                            | "union_specifier"
1949                            | "enum_specifier"
1950                            | "function_definition"
1951                            | "declaration"
1952                            | "field_declaration"
1953                            | "alias_declaration"
1954                            | "namespace_definition"
1955                    ) {
1956                        let mut template_scope = scope.clone();
1957                        template_scope.template_signature =
1958                            cpp_template_signature(node, child, self.source);
1959                        template_scope.template_metadata =
1960                            cpp_template_metadata(node, child, self.source);
1961                        if let Some(recovered) =
1962                            recover_fragmented_partial_specialization(node, child, self.source)
1963                        {
1964                            let code_unit = self.visit_named_class_like_shape(
1965                                recovered.declaration_node,
1966                                recovered.name,
1967                                None,
1968                                true,
1969                                Some(recovered.range),
1970                                None,
1971                                &template_scope,
1972                                stack,
1973                            );
1974                            self.parsed.record_materialization(
1975                                MaterializationRecord::RecoveredDeclaration {
1976                                    recovery: recovered.range,
1977                                    unit: code_unit.clone(),
1978                                },
1979                            );
1980                            let mut member_scope = template_scope.clone();
1981                            member_scope.class_unit = Some(code_unit);
1982                            member_scope.declarations_are_fields = true;
1983                            member_scope.recovered_specialization_member_scope = true;
1984                            for prefix_member in recovered.prefix_members.into_iter().rev() {
1985                                stack.push(CppWork::Node(CppNodeWork {
1986                                    node: prefix_member,
1987                                    scope: member_scope.clone(),
1988                                }));
1989                            }
1990                            for sibling in recovered.member_siblings {
1991                                self.recovered_class_sibling_scopes
1992                                    .insert(sibling.id(), member_scope.clone());
1993                            }
1994                            for following in recovered.following_declarations.into_iter().rev() {
1995                                stack.push(CppWork::Node(CppNodeWork {
1996                                    node: following,
1997                                    scope: scope.clone(),
1998                                }));
1999                            }
2000                            return;
2001                        }
2002                        stack.push(CppWork::Node(CppNodeWork {
2003                            node: child,
2004                            scope: template_scope,
2005                        }));
2006                    }
2007                }
2008            }
2009            "namespace_definition" => self.visit_namespace(node, scope, stack),
2010            "linkage_specification" => {
2011                if let Some(body) = cpp_body_node(node) {
2012                    stack.push(CppWork::Container(CppContainer {
2013                        node: body,
2014                        scope: scope.clone(),
2015                    }));
2016                } else {
2017                    stack.push(CppWork::Container(CppContainer {
2018                        node,
2019                        scope: scope.clone(),
2020                    }));
2021                }
2022            }
2023            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2024                self.visit_class_like(node, scope, stack)
2025            }
2026            "function_definition" => self.visit_function_definition(node, scope, stack),
2027            // A bare namespace-begin sentinel can make tree-sitter promote the
2028            // wrapped declaration to an ERROR node instead of the usual bogus
2029            // function_definition envelope. Keep the recovery entry point on
2030            // the same structured path for both shapes; ordinary ERROR nodes
2031            // retain their declaration-preserving wrapper traversal when the
2032            // sentinel predicate does not match.
2033            "ERROR" => {
2034                if !self.visit_sentinel_macro_region(node, scope, stack) {
2035                    self.visit_macro_swallowed_function_declarations(node, scope);
2036                    stack.push(CppWork::Container(CppContainer {
2037                        node,
2038                        scope: scope.clone(),
2039                    }));
2040                }
2041            }
2042            "declaration" => {
2043                if scope.class_unit.is_some()
2044                    && scope.declarations_are_fields
2045                    && scope.recovered_specialization_member_scope
2046                    && let Some(alias_name) =
2047                        recovered_using_declaration_alias_name(node, self.source)
2048                {
2049                    self.add_type_aliases(node, scope, vec![alias_name]);
2050                } else {
2051                    self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2052                }
2053            }
2054            "field_declaration" => self.visit_declaration(node, scope, true, stack),
2055            "type_definition" | "alias_declaration" => {
2056                self.visit_type_declaration(node, scope, stack)
2057            }
2058            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2059            "preproc_include" => self.visit_include(node),
2060            kind if preserves_declaration_scope_through_wrapper(
2061                kind,
2062                scope.class_unit.is_some(),
2063            ) =>
2064            {
2065                // A preprocessor conditional gates every declaration inside it
2066                // on a configuration this analyzer never evaluates; record the
2067                // interval so declaration state can say so (issue #1476). The
2068                // else/elif branches are children of the `preproc_if` node, so
2069                // recording the openers covers every branch.
2070                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2071                    let mut range = cpp_declaration_range(node);
2072                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2073                        range.end_byte = boundary.end_byte;
2074                        range.end_line = boundary.end_line;
2075                    }
2076                    self.parsed.record_materialization(
2077                        MaterializationRecord::ConfigurationConditional { range },
2078                    );
2079                }
2080                stack.push(CppWork::Container(CppContainer {
2081                    node,
2082                    scope: scope.clone(),
2083                }))
2084            }
2085            _ => {}
2086        }
2087    }
2088
2089    fn visit_macro_swallowed_function_declarations(
2090        &mut self,
2091        envelope: Node<'_>,
2092        scope: &ScopeInfo,
2093    ) {
2094        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2095            || envelope.kind() == "ERROR"
2096                && envelope
2097                    .parent()
2098                    .is_some_and(|parent| parent.kind() == "ERROR")
2099        {
2100            return;
2101        }
2102        let mut stack = (0..envelope.named_child_count())
2103            .filter_map(|index| envelope.named_child(index))
2104            .collect::<Vec<_>>();
2105        while let Some(node) = stack.pop() {
2106            if node.kind() == "function_declarator" {
2107                self.visit_error_swallowed_function_declaration(node, scope);
2108            }
2109            for index in 0..node.named_child_count() {
2110                if let Some(child) = node.named_child(index) {
2111                    stack.push(child);
2112                }
2113            }
2114        }
2115    }
2116
2117    fn visit_error_swallowed_function_declaration(
2118        &mut self,
2119        node: Node<'_>,
2120        scope: &ScopeInfo,
2121    ) -> bool {
2122        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2123            return false;
2124        };
2125        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2126            return false;
2127        };
2128        let root = tree.root_node();
2129        let mut cursor = root.walk();
2130        let declarations = root
2131            .named_children(&mut cursor)
2132            .filter(|child| child.kind() != "comment")
2133            .collect::<Vec<_>>();
2134        let [declaration] = declarations.as_slice() else {
2135            return false;
2136        };
2137        if declaration.kind() != "declaration"
2138            || declaration.has_error()
2139            || declaration.start_byte() != start
2140            || declaration.end_byte() != end
2141        {
2142            return false;
2143        }
2144        let recovery = cpp_recovery_window(self.source, start, end);
2145        self.record_recovered_declarations(recovery, |visitor| {
2146            visitor.run_container_work(root, scope.clone());
2147        });
2148        true
2149    }
2150
2151    fn visit_namespace<'tree>(
2152        &mut self,
2153        node: Node<'tree>,
2154        scope: &ScopeInfo,
2155        stack: &mut Vec<CppWork<'tree>>,
2156    ) {
2157        let name_node = node.child_by_field_name("name");
2158        let Some(name_node) = name_node else {
2159            if let Some(body) = cpp_body_node(node) {
2160                stack.push(CppWork::Container(CppContainer {
2161                    node: body,
2162                    scope: scope.clone(),
2163                }));
2164            }
2165            return;
2166        };
2167        // Diagnostic corpora contain deliberately ill-formed global namespace
2168        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2169        // the leading global `::` as the first anonymous child. Honor that AST
2170        // boundary instead of appending the name to the lexical namespace;
2171        // appending produced legacy names such as `outer::::outer::inner`, which
2172        // could not round-trip through the structured FqName boundary.
2173        let explicitly_global = name_node
2174            .child(0)
2175            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2176        let components = cpp_namespace_name_components(name_node, self.source);
2177        if components.is_empty() {
2178            return;
2179        }
2180        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2181        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2182        // shorthand must declare `a` as well as `a::b` -- extracting only the
2183        // innermost level left the enclosing namespace undeclared and made the
2184        // two spellings of one construct disagree (issue #1878).
2185        let mut package_name = if explicitly_global {
2186            String::new()
2187        } else {
2188            scope.package_name.clone()
2189        };
2190        let mut module = None;
2191        for component in components {
2192            let full_name = if package_name.is_empty() {
2193                component
2194            } else {
2195                format!("{package_name}::{component}")
2196            };
2197            let level = CodeUnit::new_fq(
2198                self.file.clone(),
2199                CodeUnitType::Module,
2200                "",
2201                full_name.clone(),
2202                cpp_namespace_fq(&full_name),
2203            );
2204            if !self.parsed.contains_declaration(&level) {
2205                self.parsed
2206                    .add_code_unit(level.clone(), node, self.source, None, None);
2207            }
2208            package_name = full_name;
2209            module = Some(level);
2210        }
2211
2212        let namespace_scope = ScopeInfo {
2213            package_name,
2214            module,
2215            class_unit: scope.class_unit.clone(),
2216            template_signature: scope.template_signature.clone(),
2217            template_metadata: scope.template_metadata.clone(),
2218            declarations_are_fields: false,
2219            recovered_specialization_member_scope: false,
2220            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2221        };
2222        let container = cpp_body_node(node).unwrap_or(node);
2223        stack.push(CppWork::Container(CppContainer {
2224            node: container,
2225            scope: namespace_scope,
2226        }));
2227    }
2228
2229    fn visit_class_like<'tree>(
2230        &mut self,
2231        node: Node<'tree>,
2232        scope: &ScopeInfo,
2233        stack: &mut Vec<CppWork<'tree>>,
2234    ) {
2235        let Some(name) = class_like_name(node, self.source) else {
2236            return;
2237        };
2238        self.visit_named_class_like(node, name, scope, stack);
2239    }
2240
2241    fn visit_named_class_like<'tree>(
2242        &mut self,
2243        node: Node<'tree>,
2244        name: String,
2245        scope: &ScopeInfo,
2246        stack: &mut Vec<CppWork<'tree>>,
2247    ) {
2248        let body = cpp_body_node(node);
2249        let definition_body_present = body.is_some();
2250        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2251            .then(|| extract_cpp_supertypes(node, self.source));
2252        self.visit_named_class_like_shape(
2253            node,
2254            name,
2255            body,
2256            definition_body_present,
2257            None,
2258            raw_supertypes,
2259            scope,
2260            stack,
2261        );
2262    }
2263
2264    #[allow(clippy::too_many_arguments)]
2265    fn visit_named_class_like_shape<'tree>(
2266        &mut self,
2267        declaration_node: Node<'tree>,
2268        name: String,
2269        body: Option<Node<'tree>>,
2270        definition_body_present: bool,
2271        explicit_range: Option<Range>,
2272        raw_supertypes: Option<Vec<String>>,
2273        scope: &ScopeInfo,
2274        stack: &mut Vec<CppWork<'tree>>,
2275    ) -> CodeUnit {
2276        let displaced_macro_tail = if explicit_range.is_none() {
2277            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2278        } else {
2279            None
2280        };
2281        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2282        let recovered_scope = self.scope_for_recovered_exported_class(
2283            declaration_node,
2284            &name,
2285            definition_body_present,
2286            scope,
2287        );
2288        let scope = &recovered_scope;
2289        let short_name = if let Some(parent) = &scope.class_unit {
2290            format!("{}${name}", parent.short_name())
2291        } else {
2292            name
2293        };
2294        let fq = cpp_class_fq(&scope.package_name, &short_name);
2295        let code_unit = CodeUnit::with_signature_and_fq(
2296            self.file.clone(),
2297            CodeUnitType::Class,
2298            scope.package_name.clone(),
2299            short_name,
2300            scope.template_signature.clone(),
2301            false,
2302            fq,
2303        );
2304        let has_body = definition_body_present;
2305        if !has_body && self.parsed.contains_declaration(&code_unit) {
2306            self.parsed.record_navigation_range(
2307                code_unit.clone(),
2308                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2309            );
2310            return code_unit;
2311        }
2312        if has_body {
2313            if let Some(range) = explicit_range {
2314                self.parsed
2315                    .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2316            } else {
2317                self.parsed.replace_code_unit(
2318                    code_unit.clone(),
2319                    declaration_node,
2320                    self.source,
2321                    None,
2322                    None,
2323                );
2324            }
2325        } else {
2326            self.parsed
2327                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2328        }
2329        if let Some(raw_supertypes) = raw_supertypes {
2330            self.parsed
2331                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2332        }
2333        self.parsed.add_signature(
2334            code_unit.clone(),
2335            render_cpp_type_signature(
2336                declaration_node,
2337                self.source,
2338                scope.template_signature.as_deref(),
2339            ),
2340        );
2341        if let Some(metadata) = &scope.template_metadata {
2342            let primary_short_name = if let Some(parent) = &scope.class_unit {
2343                format!("{}${}", parent.short_name(), metadata.primary_name)
2344            } else {
2345                metadata.primary_name.clone()
2346            };
2347            let primary_fq_name = CodeUnit::new(
2348                self.file.clone(),
2349                CodeUnitType::Class,
2350                scope.package_name.clone(),
2351                primary_short_name,
2352            )
2353            .fq_name();
2354            let mut metadata = metadata.clone();
2355            metadata.primary_fq_name = primary_fq_name;
2356            self.parsed
2357                .set_cpp_template_metadata(code_unit.clone(), metadata);
2358        }
2359        if let Some(parent) = &scope.class_unit {
2360            self.parsed.add_child(parent.clone(), code_unit.clone());
2361        } else if let Some(module) = &scope.module {
2362            self.parsed.add_child(module.clone(), code_unit.clone());
2363        }
2364
2365        if let Some(body) = body {
2366            let mut nested_scope = scope.clone();
2367            nested_scope.class_unit = Some(code_unit.clone());
2368            nested_scope.template_signature = scope.template_signature.clone();
2369            // Template metadata describes the class just created. It must not
2370            // leak into ordinary nested declarations in that class's body.
2371            // Recovered export-macro specializations carry a separate scope bit
2372            // for their declaration-shaped body members.
2373            nested_scope.template_metadata = None;
2374            // Export-macro class bodies recovered from a function_definition use
2375            // compound_statement children, whose direct fields are declarations.
2376            nested_scope.recovered_specialization_member_scope =
2377                scope.template_metadata.as_ref().is_some_and(|metadata| {
2378                    declaration_node.kind() == "function_definition"
2379                        && !metadata.specialization_arguments.is_empty()
2380                });
2381            nested_scope.declarations_are_fields =
2382                is_recovered_exported_class_container(declaration_node, self.source)
2383                    || nested_scope.recovered_specialization_member_scope;
2384            if let Some(displaced) = displaced_macro_tail {
2385                // A macro-shaped field without a source semicolon can make
2386                // tree-sitter consume the real class terminator as an ERROR
2387                // inside that field, then retain following namespace items as
2388                // later field-list children. Drain the proven class prefix
2389                // first and re-own only the structured tail with the outer
2390                // scope. The tail is pushed first because the work stack is
2391                // LIFO.
2392                push_cpp_sibling_range(
2393                    body,
2394                    displaced.split_index,
2395                    usize::MAX,
2396                    scope.clone(),
2397                    stack,
2398                );
2399                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2400            } else {
2401                stack.push(CppWork::Container(CppContainer {
2402                    node: body,
2403                    scope: nested_scope,
2404                }));
2405            }
2406        }
2407        if declaration_node.kind() == "enum_specifier" {
2408            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2409            if !self.has_enum_enumerator_units(&code_unit) {
2410                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2411            }
2412        }
2413        code_unit
2414    }
2415
2416    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2417        let prefix = format!("{}.", parent.short_name());
2418        self.parsed.declarations().iter().any(|unit| {
2419            unit.kind() == CodeUnitType::Field
2420                && unit.source() == parent.source()
2421                && unit.package_name() == parent.package_name()
2422                && unit.short_name().starts_with(&prefix)
2423        })
2424    }
2425
2426    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2427        walk_named_tree_preorder(node, false, |child| {
2428            if child.kind() != "enumerator" {
2429                return WalkControl::Continue;
2430            }
2431            let Some(name_node) = child.child_by_field_name("name") else {
2432                return WalkControl::Continue;
2433            };
2434            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2435            if name.is_empty() {
2436                return WalkControl::Continue;
2437            }
2438            let code_unit = CodeUnit::new_fq(
2439                self.file.clone(),
2440                CodeUnitType::Field,
2441                scope.package_name.clone(),
2442                format!("{}.{}", parent.short_name(), name),
2443                parent
2444                    .fq()
2445                    .clone()
2446                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2447            );
2448            if self.parsed.contains_declaration(&code_unit) {
2449                return WalkControl::Continue;
2450            }
2451            self.parsed.add_code_unit(
2452                code_unit.clone(),
2453                child,
2454                self.source,
2455                Some(parent.clone()),
2456                None,
2457            );
2458            self.parsed.add_signature(
2459                code_unit,
2460                normalize_cpp_whitespace(node_text(child, self.source)),
2461            );
2462            WalkControl::Continue
2463        });
2464    }
2465
2466    fn visit_enum_enumerators_from_text(
2467        &mut self,
2468        node: Node<'_>,
2469        scope: &ScopeInfo,
2470        parent: &CodeUnit,
2471    ) {
2472        let text = node_text(node, self.source);
2473        let Some((_, body)) = text.split_once('{') else {
2474            return;
2475        };
2476        let Some((body, _)) = body.rsplit_once('}') else {
2477            return;
2478        };
2479        for entry in body.split(',') {
2480            let trimmed = entry.trim();
2481            let name = trimmed
2482                .split('=')
2483                .next()
2484                .unwrap_or("")
2485                .split_whitespace()
2486                .next()
2487                .unwrap_or("");
2488            if name.is_empty() {
2489                continue;
2490            }
2491            let code_unit = CodeUnit::new_fq(
2492                self.file.clone(),
2493                CodeUnitType::Field,
2494                scope.package_name.clone(),
2495                format!("{}.{}", parent.short_name(), name),
2496                parent
2497                    .fq()
2498                    .clone()
2499                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
2500            );
2501            if self.parsed.contains_declaration(&code_unit) {
2502                continue;
2503            }
2504            self.parsed.add_code_unit(
2505                code_unit.clone(),
2506                node,
2507                self.source,
2508                Some(parent.clone()),
2509                None,
2510            );
2511            self.parsed.add_signature(code_unit, trimmed.to_string());
2512        }
2513    }
2514
2515    fn visit_function_definition<'tree>(
2516        &mut self,
2517        node: Node<'tree>,
2518        scope: &ScopeInfo,
2519        stack: &mut Vec<CppWork<'tree>>,
2520    ) {
2521        // A file-scope object-like macro sentinel the parser cannot see (issue
2522        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
2523        // prefixes as a bogus `function_definition` that swallows real namespaces,
2524        // classes, and members. Reparse the swallowed interior as C++ items so the
2525        // ordinary declaration visitors index it with byte/line-exact ownership.
2526        if self.visit_sentinel_macro_region(node, scope, stack) {
2527            return;
2528        }
2529        if node.has_error() {
2530            self.visit_macro_swallowed_function_declarations(node, scope);
2531        }
2532        if let Some((class_node, name, raw_supertypes)) =
2533            recover_exported_class_function_definition(node, self.source)
2534        {
2535            let body = cpp_body_node(class_node);
2536            let fragmented = cpp_body_node(node)
2537                .and_then(|body| fragmented_export_function_body_region(node, body, self.source));
2538            // The recovery tuple's first node is the class-like type when the
2539            // parser exposes one, but the synthetic wrapper owns the compound
2540            // statement that contains the truncated class body. Use the
2541            // wrapper body for fragmented-member detection; retain the
2542            // class-node body for the ordinary (non-fragmented) path below.
2543            if let Some(fragmented) = fragmented {
2544                // The lifted sibling no longer sits below the parser-visible
2545                // namespace node. Restore the current parent scope when the
2546                // ordinary work walk reaches that class.
2547                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
2548                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
2549                {
2550                    let mut boundary_scope = scope.clone();
2551                    for sibling in cpp_following_named_siblings(node, self.source) {
2552                        if sibling.start_byte() >= boundary.start_byte() {
2553                            break;
2554                        }
2555                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
2556                            boundary_scope.visible_using_namespaces.push(namespace);
2557                        }
2558                    }
2559                    self.recovered_class_sibling_scopes
2560                        .insert(boundary.id(), boundary_scope);
2561                }
2562                let mut recovered_constructor = None;
2563                let mut recovered_prefix_tree = None;
2564                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
2565                {
2566                    Some(FragmentedExportMembers::Complete(tree)) => {
2567                        if let Some(body) = body
2568                            && let Some(range) =
2569                                cpp_reparsed_synthetic_initializer_constructor_range(
2570                                    tree.root_node(),
2571                                    &name,
2572                                    self.source,
2573                                    body.end_byte(),
2574                                )
2575                        {
2576                            recovered_constructor = Some(range);
2577                            recovered_prefix_tree = Some(tree);
2578                            None
2579                        } else {
2580                            Some(FragmentedExportMembers::Complete(tree))
2581                        }
2582                    }
2583                    outcome => outcome,
2584                };
2585                let mut class_stack = Vec::new();
2586                let class_unit = self.visit_named_class_like_shape(
2587                    class_node,
2588                    name,
2589                    None,
2590                    true,
2591                    Some(fragmented.class_range),
2592                    raw_supertypes,
2593                    scope,
2594                    &mut class_stack,
2595                );
2596                self.parsed
2597                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
2598                        recovery: fragmented.class_range,
2599                        unit: class_unit.clone(),
2600                    });
2601                let complete = outcome.is_some_and(|outcome| {
2602                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
2603                });
2604                if complete {
2605                    self.consumed_fragment_regions
2606                        .push((node.start_byte(), fragmented.class_range.end_byte));
2607                } else {
2608                    // The reparse can fail when the first constructor or a
2609                    // method body is split into statement-shaped siblings.
2610                    // Keep the recovered class envelope, but do not visit the
2611                    // synthetic wrapper body: its initializer expressions can
2612                    // look like same-named member functions (for example
2613                    // `Token.location(loc)`). Re-own only the original sibling
2614                    // nodes that fall inside the proven class range. Their CST
2615                    // shapes retain the real field/function kinds and ranges.
2616                    let member_scope = ScopeInfo {
2617                        package_name: class_unit.package_name().to_string(),
2618                        module: scope.module.clone(),
2619                        class_unit: Some(class_unit.clone()),
2620                        template_signature: scope.template_signature.clone(),
2621                        template_metadata: None,
2622                        declarations_are_fields: true,
2623                        recovered_specialization_member_scope: false,
2624                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
2625                    };
2626                    for candidate in cpp_following_named_siblings(node, self.source) {
2627                        if candidate.start_byte() >= fragmented.reparse_end {
2628                            break;
2629                        }
2630                        if cpp_fragment_sibling_is_class_member(
2631                            candidate,
2632                            fragmented.reparse_end,
2633                            self.source,
2634                        ) {
2635                            self.recovered_class_sibling_scopes
2636                                .insert(candidate.id(), member_scope.clone());
2637                        }
2638                    }
2639                    if let Some(range) = recovered_constructor
2640                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
2641                    {
2642                        self.visit_recovered_fragment_prefix_members(
2643                            prefix_tree.root_node(),
2644                            range.start,
2645                            &class_unit,
2646                            scope,
2647                        );
2648                        self.visit_recovered_fragment_constructor(
2649                            range,
2650                            body,
2651                            class_node,
2652                            &class_unit,
2653                            scope,
2654                        );
2655                    }
2656                }
2657                stack.extend(class_stack);
2658                return;
2659            }
2660            let mut stack = Vec::new();
2661            let class_unit = self.visit_named_class_like_shape(
2662                class_node,
2663                name,
2664                body,
2665                body.is_some(),
2666                None,
2667                raw_supertypes,
2668                scope,
2669                &mut stack,
2670            );
2671            self.parsed
2672                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2673                    recovery: cpp_declaration_range(node),
2674                    unit: class_unit,
2675                });
2676            // Issue #1524: the bogus `function_definition` body can run past
2677            // the class's true closing brace (the parse ends it with a
2678            // zero-width `MISSING "}"`), swallowing following namespace-scope
2679            // siblings -- they would index as members of the recovered class.
2680            // When the body's text-balanced close lands before the body's own
2681            // end, re-own the swallowed tail with the outer scope instead.
2682            if let Some(body) = body
2683                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
2684                && class_close < body.end_byte()
2685            {
2686                let split = {
2687                    let mut cursor = body.walk();
2688                    body.named_children(&mut cursor)
2689                        .position(|child| child.start_byte() > class_close)
2690                };
2691                if let Some(split) = split {
2692                    // The seeded work is a single Container over the whole
2693                    // body with the class scope; replace it with the bounded
2694                    // head (class scope) plus the swallowed tail (outer
2695                    // scope). Push tail first so the head drains first.
2696                    let seeded = stack.pop();
2697                    match seeded {
2698                        Some(CppWork::Container(container)) => {
2699                            push_cpp_sibling_range(
2700                                body,
2701                                split,
2702                                usize::MAX,
2703                                scope.clone(),
2704                                &mut stack,
2705                            );
2706                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
2707                        }
2708                        // visit_named_class_like_shape always seeds exactly
2709                        // one Container when a body is present.
2710                        _ => unreachable!("exported-class seed is always one Container"),
2711                    }
2712                }
2713            }
2714            while let Some(work) = stack.pop() {
2715                match work {
2716                    CppWork::Container(container) => {
2717                        push_cpp_container_work(container.node, container.scope, &mut stack);
2718                    }
2719                    CppWork::Siblings(siblings) => {
2720                        advance_cpp_siblings(siblings, self.source, &mut stack);
2721                    }
2722                    CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2723                }
2724            }
2725            return;
2726        }
2727        let recovered_constraint_constructor =
2728            cpp_recovered_template_macro_constructor(node, self.source);
2729        let declarator = recovered_constraint_constructor
2730            .map(|(declarator, _)| declarator)
2731            .or_else(|| node.child_by_field_name("declarator"));
2732        let Some(declarator) = declarator else {
2733            self.visit_malformed_function_definition_container(node, scope, stack);
2734            return;
2735        };
2736        let Some(function_declarator) = extract_function_declarator(declarator) else {
2737            self.visit_malformed_function_definition_container(node, scope, stack);
2738            return;
2739        };
2740        let Some(mut function) = extract_function_info(function_declarator, self.source, scope)
2741        else {
2742            self.visit_malformed_function_definition_container(node, scope, stack);
2743            return;
2744        };
2745        if let Some((_, template_parameter)) = recovered_constraint_constructor {
2746            function.signature = format!(
2747                "template <{}>{}",
2748                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
2749                function.signature
2750            );
2751        }
2752        let code_unit = function.code_unit(self.file.clone());
2753        // Keep an earlier same-file prototype as another physical occurrence
2754        // of this callable. `CodeUnit` already identifies the role-neutral
2755        // overload, while ranges and signature metadata describe its
2756        // declaration/definition occurrences.
2757        self.parsed
2758            .add_code_unit(code_unit.clone(), node, self.source, None, None);
2759        let signature = if recovered_constraint_constructor.is_some() {
2760            normalize_cpp_whitespace(node_text(function_declarator, self.source))
2761        } else {
2762            render_cpp_function_display_signature_from_node(
2763                node,
2764                self.source,
2765                scope.template_signature.as_deref(),
2766                true,
2767            )
2768        };
2769        self.parsed.add_signature_with_metadata(
2770            code_unit.clone(),
2771            cpp_signature_metadata(signature, function_declarator, self.source)
2772                .with_declaration_only(false)
2773                .with_callable_linkage(cpp_callable_linkage(node, self.source)),
2774        );
2775        if let Some(parent) = &scope.class_unit {
2776            self.parsed.add_child(parent.clone(), code_unit);
2777        } else if let Some(module) = &scope.module {
2778            self.parsed.add_child(module.clone(), code_unit);
2779        }
2780    }
2781
2782    /// Recover the namespace lost when tree-sitter promotes an export-macro
2783    /// class definition to a root-level `function_definition`.  Only a
2784    /// body-bearing, top-level recovery may borrow a namespace, and only when
2785    /// one earlier namespace-scope forward declaration proves the identity.
2786    fn scope_for_recovered_exported_class(
2787        &self,
2788        node: Node<'_>,
2789        name: &str,
2790        definition_body_present: bool,
2791        scope: &ScopeInfo,
2792    ) -> ScopeInfo {
2793        if !definition_body_present
2794            || !scope.package_name.is_empty()
2795            || scope.class_unit.is_some()
2796            || !(is_recovered_exported_class_container(node, self.source)
2797                || matches!(node.kind(), "declaration" | "field_declaration")
2798                    && recover_exported_class_declaration(node, self.source).is_some()
2799                || matches!(
2800                    node.kind(),
2801                    "class_specifier" | "struct_specifier" | "union_specifier"
2802                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
2803                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
2804                        name_node,
2805                        self.source,
2806                    )))
2807                }) || node.parent().is_some_and(|parent| {
2808                    matches!(parent.kind(), "declaration" | "field_declaration")
2809                        && recover_exported_class_declaration(parent, self.source).is_some()
2810                        || is_recovered_exported_class_container(parent, self.source)
2811                })) && class_like_name(node, self.source).as_deref() == Some(name))
2812        {
2813            return scope.clone();
2814        }
2815        let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
2816        else {
2817            return scope.clone();
2818        };
2819
2820        let module = CodeUnit::new_fq(
2821            self.file.clone(),
2822            CodeUnitType::Module,
2823            "",
2824            package_name.clone(),
2825            cpp_namespace_fq(&package_name),
2826        );
2827        let mut recovered = scope.clone();
2828        recovered.package_name = package_name;
2829        recovered.module = Some(module);
2830        recovered
2831    }
2832
2833    fn visit_malformed_function_definition_container<'tree>(
2834        &mut self,
2835        node: Node<'tree>,
2836        scope: &ScopeInfo,
2837        stack: &mut Vec<CppWork<'tree>>,
2838    ) {
2839        let Some(body) = cpp_body_node(node) else {
2840            return;
2841        };
2842        if !cpp_contains_namespace_definition(body) {
2843            return;
2844        }
2845        stack.push(CppWork::Container(CppContainer {
2846            node: body,
2847            scope: scope.clone(),
2848        }));
2849    }
2850
2851    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
2852    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
2853    /// emits for a sentinel-prefixed region, reparse the interior after the
2854    /// sentinel identifier as real C++ items -- confined to the region so
2855    /// every reparsed node keeps its original byte/line position -- and run the
2856    /// ordinary container visitation over the result. Returns `true` when it fired
2857    /// (the caller must then skip normal function processing). Nested sentinel
2858    /// regions recover recursively: the reparsed interior is walked through the
2859    /// same `visit_function_definition` path, so a sentinel inside the region hits
2860    /// this recovery again.
2861    /// Runs `reparse_walk` and records every declaration it mints as a
2862    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
2863    /// `recovery` (issue #1657). A reparsed sentinel region has no single
2864    /// recovered envelope unit: the ordinary visitors mint namespaces,
2865    /// classes, and members directly from the reparsed tree, so the walk's
2866    /// declaration delta is the recovered set. Records are ordered by
2867    /// declaration start byte so the parse product stays deterministic.
2868    fn record_recovered_declarations(
2869        &mut self,
2870        recovery: Range,
2871        reparse_walk: impl FnOnce(&mut Self),
2872    ) {
2873        let before = self.parsed.declarations().clone();
2874        reparse_walk(self);
2875        let mut minted: Vec<CodeUnit> = self
2876            .parsed
2877            .declarations()
2878            .iter()
2879            .filter(|unit| !before.contains(*unit))
2880            .cloned()
2881            .collect();
2882        minted.sort_by_cached_key(|unit| {
2883            let start = self
2884                .parsed
2885                .declaration_ranges(unit)
2886                .first()
2887                .map(|range| range.start_byte)
2888                .unwrap_or(usize::MAX);
2889            (start, unit.fq_name().to_string())
2890        });
2891        for unit in minted {
2892            self.parsed
2893                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2894                    recovery,
2895                    unit,
2896                });
2897        }
2898    }
2899
2900    fn visit_sentinel_macro_region<'tree>(
2901        &mut self,
2902        node: Node<'tree>,
2903        scope: &ScopeInfo,
2904        stack: &mut Vec<CppWork<'tree>>,
2905    ) -> bool {
2906        if self.visit_nested_namespace_sentinel(node, scope) {
2907            return true;
2908        }
2909        if let Some((
2910            reparse_start,
2911            class_start,
2912            body_start,
2913            class_close_start,
2914            class_close_end,
2915            class_close_line,
2916        )) = cpp_sentinel_macro_class_region(node, self.source)
2917        {
2918            let Some(class_tree) =
2919                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
2920            else {
2921                return false;
2922            };
2923            let class_root = class_tree.root_node();
2924            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
2925            let Some(reparsed_class) =
2926                cpp_sentinel_reparsed_class(class_root, template_node, self.source)
2927            else {
2928                return false;
2929            };
2930            let class_node = reparsed_class.declaration_node;
2931            let name = reparsed_class.name;
2932            let mut class_scope = scope.clone();
2933            if let Some(template_node) = template_node {
2934                class_scope.template_signature =
2935                    cpp_template_signature(template_node, class_node, self.source);
2936                class_scope.template_metadata =
2937                    cpp_template_metadata(template_node, class_node, self.source);
2938            }
2939            let Some(body_tree) =
2940                cpp_reparse_region_items(self.source, body_start, class_close_start)
2941            else {
2942                return false;
2943            };
2944            let raw_supertypes = reparsed_class.raw_supertypes;
2945            let class_range = Range {
2946                start_byte: class_start,
2947                end_byte: class_close_end,
2948                start_line: class_node.start_position().row + 1,
2949                end_line: class_close_line,
2950            };
2951            let class_scope =
2952                self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
2953            let mut class_stack = Vec::new();
2954            let class_unit = self.visit_named_class_like_shape(
2955                class_node,
2956                name,
2957                None,
2958                true,
2959                Some(class_range),
2960                raw_supertypes,
2961                &class_scope,
2962                &mut class_stack,
2963            );
2964            self.parsed
2965                .record_materialization(MaterializationRecord::RecoveredDeclaration {
2966                    recovery: class_range,
2967                    unit: class_unit.clone(),
2968                });
2969            let member_scope = ScopeInfo {
2970                package_name: class_scope.package_name.clone(),
2971                module: class_scope.module.clone(),
2972                class_unit: Some(class_unit),
2973                template_signature: class_scope.template_signature.clone(),
2974                template_metadata: None,
2975                declarations_are_fields: true,
2976                recovered_specialization_member_scope: false,
2977                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
2978            };
2979            self.run_container_work(body_tree.root_node(), member_scope);
2980            // Register only after the padded body reparse: its nodes deliberately
2981            // retain offsets inside the consumed region and must be visited first.
2982            self.consumed_fragment_regions
2983                .push((node.start_byte(), class_close_end));
2984            // An ERROR envelope can hold real sibling declarations after the
2985            // recovered class's close (the suffix-reparse boundary in
2986            // `cpp_sentinel_macro_class_region` partitions, it does not
2987            // consume). Walk the envelope's remaining children normally; the
2988            // consumed region above keeps the recovered class from being
2989            // indexed twice.
2990            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
2991                stack.push(CppWork::Container(CppContainer {
2992                    node,
2993                    scope: scope.clone(),
2994                }));
2995            }
2996            return true;
2997        }
2998        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
2999            return false;
3000        };
3001        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3002            return false;
3003        };
3004        let root = tree.root_node();
3005        if !cpp_reparsed_items_are_indexable(root, self.source) {
3006            return false;
3007        }
3008        let recovery = cpp_recovery_window(self.source, start, end);
3009        self.record_recovered_declarations(recovery, |visitor| {
3010            visitor.visit_container(
3011                root,
3012                &scope.package_name,
3013                scope.module.clone(),
3014                scope.class_unit.clone(),
3015                scope.template_signature.clone(),
3016                scope.visible_using_namespaces.clone(),
3017            );
3018        });
3019        if end > node.end_byte() {
3020            self.consumed_fragment_regions
3021                .push((node.start_byte(), end));
3022        } else if node.kind() == "ERROR" && node.end_byte() > end {
3023            // The sentinel region ended at the first recovered class-like item
3024            // but the ERROR envelope keeps real sibling declarations after it
3025            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
3026            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
3027            // envelope's remaining children normally; the consumed region
3028            // keeps the reparsed prefix from being indexed twice.
3029            self.consumed_fragment_regions
3030                .push((node.start_byte(), end));
3031            stack.push(CppWork::Container(CppContainer {
3032                node,
3033                scope: scope.clone(),
3034            }));
3035        }
3036        true
3037    }
3038
3039    /// Re-own complete class declarations from the structured Abseil
3040    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
3041    /// its direct CST children already prove both namespace components and the
3042    /// class bodies, so the ordinary class/member visitor can retain ownership
3043    /// and exact source ranges without admitting unrelated callable bodies.
3044    fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3045        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3046            return false;
3047        };
3048
3049        let mut package_name = scope.package_name.clone();
3050        let mut module = scope.module.clone();
3051        for component in recovered.namespace_components {
3052            package_name = if package_name.is_empty() {
3053                component
3054            } else {
3055                format!("{package_name}::{component}")
3056            };
3057            let namespace_module = CodeUnit::new_fq(
3058                self.file.clone(),
3059                CodeUnitType::Module,
3060                "",
3061                package_name.clone(),
3062                cpp_namespace_fq(&package_name),
3063            );
3064            if !self.parsed.contains_declaration(&namespace_module) {
3065                self.parsed.add_code_unit(
3066                    namespace_module.clone(),
3067                    recovered.function,
3068                    self.source,
3069                    None,
3070                    None,
3071                );
3072            }
3073            module = Some(namespace_module);
3074        }
3075
3076        let recovered_scope = ScopeInfo {
3077            package_name,
3078            module,
3079            class_unit: scope.class_unit.clone(),
3080            template_signature: scope.template_signature.clone(),
3081            template_metadata: scope.template_metadata.clone(),
3082            declarations_are_fields: false,
3083            recovered_specialization_member_scope: false,
3084            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3085        };
3086        if let Some(fragmented) =
3087            cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3088        {
3089            let mut class_scope = recovered_scope.clone();
3090            if let Some(template_node) = fragmented.template_node {
3091                class_scope.template_signature =
3092                    cpp_template_signature(template_node, fragmented.class_node, self.source);
3093                class_scope.template_metadata =
3094                    cpp_template_metadata(template_node, fragmented.class_node, self.source);
3095            }
3096            let raw_supertypes = matches!(
3097                fragmented.class_node.kind(),
3098                "class_specifier" | "struct_specifier"
3099            )
3100            .then(|| extract_cpp_supertypes(fragmented.class_node, self.source));
3101            if let Some(outcome) = self
3102                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3103            {
3104                let mut class_stack = Vec::new();
3105                let class_unit = self.visit_named_class_like_shape(
3106                    fragmented.class_node,
3107                    fragmented.name.clone(),
3108                    None,
3109                    true,
3110                    Some(fragmented.fragmented.class_range),
3111                    raw_supertypes,
3112                    &class_scope,
3113                    &mut class_stack,
3114                );
3115                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3116                    self.consumed_fragment_regions.push((
3117                        fragmented.consumed_start,
3118                        fragmented.fragmented.class_range.end_byte,
3119                    ));
3120                }
3121            }
3122        }
3123        // The class requirement above is the admission gate; once admitted,
3124        // traverse the whole proven inner namespace body so sibling aliases,
3125        // functions, and variables are not silently discarded.
3126        self.run_container_work(recovered.body, recovered_scope);
3127        true
3128    }
3129
3130    fn visit_declaration<'tree>(
3131        &mut self,
3132        node: Node<'tree>,
3133        scope: &ScopeInfo,
3134        in_class_body: bool,
3135        stack: &mut Vec<CppWork<'tree>>,
3136    ) {
3137        if self.visit_sentinel_macro_region(node, scope, stack) {
3138            return;
3139        }
3140        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3141            !cpp_active_template_type_parameter(
3142                node,
3143                node_text(declarator, self.source),
3144                self.source,
3145            )
3146        }) {
3147            return;
3148        }
3149        if in_class_body
3150            && let Some(parent) = scope.class_unit.as_ref()
3151            && let Some(call) =
3152                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3153        {
3154            self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3155            return;
3156        }
3157        if in_class_body
3158            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3159        {
3160            self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3161            return;
3162        }
3163        if in_class_body
3164            && let Some(declarators) =
3165                recovered_macro_qualified_field_declarators(node, self.source)
3166        {
3167            for declarator in declarators {
3168                self.visit_variable_declaration(node, declarator, scope, true);
3169            }
3170            return;
3171        }
3172        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3173        if !recovered_alias_names.is_empty() {
3174            self.add_type_aliases(node, scope, recovered_alias_names);
3175            return;
3176        }
3177
3178        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3179            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3180                // Issue #938: the members tree-sitter scattered out of the fragmented
3181                // multiple-base export node are reparsed from their true body region
3182                // and re-owned as members of the recovered class, with an explicit
3183                // navigation range spanning to the displaced closing brace.
3184                if let Some(outcome) =
3185                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3186                {
3187                    let consumed_region = (
3188                        recovered.declaration_node.end_byte(),
3189                        fragmented.class_range.end_byte,
3190                    );
3191                    let code_unit = self.visit_named_class_like_shape(
3192                        recovered.declaration_node,
3193                        recovered.name,
3194                        None,
3195                        true,
3196                        Some(fragmented.class_range),
3197                        recovered.raw_supertypes,
3198                        scope,
3199                        stack,
3200                    );
3201                    self.parsed.record_materialization(
3202                        MaterializationRecord::RecoveredDeclaration {
3203                            recovery: fragmented.class_range,
3204                            unit: code_unit.clone(),
3205                        },
3206                    );
3207                    let consume_fragment =
3208                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3209                    // Everything between the fragmented declaration and its displaced
3210                    // closing brace now belongs to the recovered class; keep the
3211                    // ordinary walk from re-indexing those scattered siblings at top
3212                    // level. Register the consumed region only after indexing because
3213                    // the reparsed nodes retain byte offsets inside that same region.
3214                    if consume_fragment {
3215                        self.consumed_fragment_regions.push(consumed_region);
3216                    }
3217                    return;
3218                }
3219            }
3220            let uses_initializer_body = recovered.uses_initializer_body;
3221            let definition_body_present = recovered.body.is_some();
3222            let class_unit = self.visit_named_class_like_shape(
3223                recovered.declaration_node,
3224                recovered.name,
3225                recovered.body,
3226                definition_body_present,
3227                None,
3228                recovered.raw_supertypes,
3229                scope,
3230                stack,
3231            );
3232            self.parsed
3233                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3234                    recovery: cpp_declaration_range(node),
3235                    unit: class_unit,
3236                });
3237            if uses_initializer_body {
3238                return;
3239            }
3240        }
3241
3242        let mut handled_function = false;
3243        let mut handled_declarator = false;
3244        let mut cursor = node.walk();
3245        for child in node.named_children(&mut cursor) {
3246            if matches!(
3247                child.kind(),
3248                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3249            ) {
3250                // A named class-like definition remains a declaration even when
3251                // the same statement also declares an object, for example
3252                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3253                // declaration's type and `kind` as its declarator.  Dropping the
3254                // type here loses both its nested owner and every later lexical
3255                // reference to it.  A body is the structured proof that this is
3256                // a definition rather than an elaborated type use such as
3257                // `class Kind value;`.
3258                if cpp_body_node(child).is_some() {
3259                    self.visit_class_like(child, scope, stack);
3260                }
3261                continue;
3262            }
3263        }
3264
3265        let mut cursor = node.walk();
3266        for child in node.children_by_field_name("declarator", &mut cursor) {
3267            if crate::structural::is_recovered_designator_init_declarator(child) {
3268                handled_declarator = true;
3269                continue;
3270            }
3271            if let Some(kind) = classify_declarator(child) {
3272                handled_declarator = true;
3273                match kind {
3274                    DeclaratorKind::Function(function_declarator) => {
3275                        handled_function = true;
3276                        self.visit_function_declaration(node, function_declarator, scope);
3277                    }
3278                    DeclaratorKind::Variable(variable_declarator) => {
3279                        self.visit_variable_declaration(
3280                            node,
3281                            variable_declarator,
3282                            scope,
3283                            in_class_body,
3284                        );
3285                    }
3286                }
3287            }
3288        }
3289
3290        if !handled_declarator {
3291            let mut cursor = node.walk();
3292            for child in node.named_children(&mut cursor) {
3293                if crate::structural::is_recovered_designator_init_declarator(child) {
3294                    handled_declarator = true;
3295                    continue;
3296                }
3297                if !is_unfielded_declarator_candidate(child) {
3298                    continue;
3299                }
3300                let Some(kind) = classify_declarator(child) else {
3301                    continue;
3302                };
3303                handled_declarator = true;
3304                match kind {
3305                    DeclaratorKind::Function(function_declarator) => {
3306                        handled_function = true;
3307                        self.visit_function_declaration(node, function_declarator, scope);
3308                    }
3309                    DeclaratorKind::Variable(variable_declarator) => {
3310                        self.visit_variable_declaration(
3311                            node,
3312                            variable_declarator,
3313                            scope,
3314                            in_class_body,
3315                        );
3316                    }
3317                }
3318            }
3319        }
3320
3321        if handled_function {
3322            return;
3323        }
3324
3325        if !handled_declarator {
3326            if in_class_body {
3327                self.visit_class_members_from_declaration(node, scope);
3328            } else {
3329                self.visit_global_variables_from_declaration(node, scope);
3330            }
3331        }
3332    }
3333
3334    fn visit_function_declaration(
3335        &mut self,
3336        declaration_node: Node<'_>,
3337        declarator: Node<'_>,
3338        scope: &ScopeInfo,
3339    ) {
3340        let Some(function) = extract_function_info(declarator, self.source, scope) else {
3341            return;
3342        };
3343        let code_unit =
3344            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3345        if self.parsed.contains_declaration(&code_unit) {
3346            self.parsed
3347                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3348            return;
3349        }
3350        self.parsed
3351            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3352        let signature = render_cpp_function_display_signature_from_node(
3353            declaration_node,
3354            self.source,
3355            scope.template_signature.as_deref(),
3356            false,
3357        );
3358        self.parsed.add_signature_with_metadata(
3359            code_unit.clone(),
3360            cpp_signature_metadata(signature, declarator, self.source)
3361                .with_declaration_only(true)
3362                .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3363        );
3364        if let Some(parent) = &scope.class_unit {
3365            self.parsed.add_child(parent.clone(), code_unit);
3366        } else if let Some(module) = &scope.module {
3367            self.parsed.add_child(module.clone(), code_unit);
3368        }
3369    }
3370
3371    fn visit_recovered_macro_qualified_function_declaration(
3372        &mut self,
3373        declaration_node: Node<'_>,
3374        call: Node<'_>,
3375        scope: &ScopeInfo,
3376    ) {
3377        let Some(parent) = &scope.class_unit else {
3378            return;
3379        };
3380        let Some(name_node) = call.child_by_field_name("function") else {
3381            return;
3382        };
3383        let Some(arguments) = call.child_by_field_name("arguments") else {
3384            return;
3385        };
3386        let Some((signature, parameter_labels)) =
3387            recovered_macro_qualified_function_parameters(arguments, self.source)
3388        else {
3389            return;
3390        };
3391        let arity = parameter_labels.len();
3392        let function = FunctionInfo {
3393            package_name: scope.package_name.clone(),
3394            owner_path: Some(parent.short_name().to_string()),
3395            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3396            signature,
3397        };
3398        if function.name.is_empty() {
3399            return;
3400        }
3401        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3402        if self.parsed.contains_declaration(&code_unit) {
3403            self.parsed
3404                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3405            return;
3406        }
3407        self.parsed
3408            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3409        let signature_label = render_cpp_function_display_signature_from_node(
3410            declaration_node,
3411            self.source,
3412            scope.template_signature.as_deref(),
3413            false,
3414        );
3415        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3416            .with_declaration_only(true)
3417            .with_callable_arity(CallableArity::exact(arity))
3418            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3419        self.parsed
3420            .add_signature_with_metadata(code_unit.clone(), metadata);
3421        self.parsed.add_child(parent.clone(), code_unit);
3422    }
3423
3424    fn visit_recovered_macro_qualified_constructor_definition(
3425        &mut self,
3426        declaration_node: Node<'_>,
3427        call: Node<'_>,
3428        scope: &ScopeInfo,
3429    ) {
3430        let Some(parent) = &scope.class_unit else {
3431            return;
3432        };
3433        let Some(arguments) = call.child_by_field_name("arguments") else {
3434            return;
3435        };
3436        let Some((mut signature, parameter_labels)) =
3437            recovered_macro_qualified_function_parameters(arguments, self.source)
3438        else {
3439            return;
3440        };
3441        if let Some(template_signature) = &scope.template_signature {
3442            signature = format!("{template_signature}{signature}");
3443        }
3444        let arity = parameter_labels.len();
3445        let function = FunctionInfo {
3446            package_name: scope.package_name.clone(),
3447            owner_path: Some(parent.short_name().to_string()),
3448            name: parent.identifier().to_string(),
3449            signature,
3450        };
3451        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3452        self.parsed
3453            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3454        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
3455        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3456            .with_declaration_only(false)
3457            .with_callable_arity(CallableArity::exact(arity))
3458            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3459        self.parsed
3460            .add_signature_with_metadata(code_unit.clone(), metadata);
3461        self.parsed.add_child(parent.clone(), code_unit);
3462    }
3463
3464    fn visit_variable_declaration(
3465        &mut self,
3466        declaration_node: Node<'_>,
3467        declarator: Node<'_>,
3468        scope: &ScopeInfo,
3469        in_class_body: bool,
3470    ) {
3471        let Some(name) = extract_variable_name(declarator, self.source) else {
3472            return;
3473        };
3474        let short_name = if in_class_body {
3475            let Some(parent) = &scope.class_unit else {
3476                return;
3477            };
3478            format!("{}.{}", parent.short_name(), name)
3479        } else {
3480            name
3481        };
3482        let fq = cpp_member_fq(&scope.package_name, &short_name);
3483        let code_unit = CodeUnit::new_fq(
3484            self.file.clone(),
3485            CodeUnitType::Field,
3486            scope.package_name.clone(),
3487            short_name,
3488            fq,
3489        );
3490        if self.parsed.contains_declaration(&code_unit) {
3491            return;
3492        }
3493        self.parsed
3494            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3495        self.parsed.add_signature_with_metadata(
3496            code_unit.clone(),
3497            SignatureMetadata::new(
3498                render_cpp_field_signature(declaration_node, declarator, self.source),
3499                Vec::new(),
3500            )
3501            .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
3502        );
3503        if let Some(parent) = &scope.class_unit {
3504            self.parsed.add_child(parent.clone(), code_unit);
3505        } else if let Some(module) = &scope.module {
3506            self.parsed.add_child(module.clone(), code_unit);
3507        }
3508    }
3509
3510    fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3511        let mut cursor = node.walk();
3512        for child in node.named_children(&mut cursor) {
3513            if child.kind() == "init_declarator"
3514                && let Some(inner) = child.child_by_field_name("declarator")
3515            {
3516                self.visit_variable_declaration(node, inner, scope, true);
3517            } else if matches!(
3518                child.kind(),
3519                "identifier"
3520                    | "field_identifier"
3521                    | "pointer_declarator"
3522                    | "reference_declarator"
3523                    | "array_declarator"
3524                    | "parenthesized_declarator"
3525            ) {
3526                self.visit_variable_declaration(node, child, scope, true);
3527            }
3528        }
3529    }
3530
3531    fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
3532        let mut cursor = node.walk();
3533        for child in node.named_children(&mut cursor) {
3534            if child.kind() == "init_declarator"
3535                && let Some(inner) = child.child_by_field_name("declarator")
3536            {
3537                self.visit_variable_declaration(node, inner, scope, false);
3538            } else if matches!(
3539                child.kind(),
3540                "identifier"
3541                    | "field_identifier"
3542                    | "pointer_declarator"
3543                    | "reference_declarator"
3544                    | "array_declarator"
3545                    | "parenthesized_declarator"
3546            ) {
3547                self.visit_variable_declaration(node, child, scope, false);
3548            }
3549        }
3550    }
3551
3552    fn visit_include(&mut self, node: Node<'_>) {
3553        let raw = normalize_cpp_whitespace(node_text(node, self.source));
3554        self.parsed.imports.push(ImportInfo {
3555            raw_snippet: raw,
3556            is_wildcard: false,
3557            is_global: false,
3558            identifier: None,
3559            alias: None,
3560            path: None,
3561            binder_span: None,
3562        });
3563    }
3564
3565    fn visit_type_declaration<'tree>(
3566        &mut self,
3567        node: Node<'tree>,
3568        scope: &ScopeInfo,
3569        stack: &mut Vec<CppWork<'tree>>,
3570    ) {
3571        if let Some(type_node) = node.child_by_field_name("type")
3572            && matches!(
3573                type_node.kind(),
3574                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3575            )
3576        {
3577            self.visit_class_like(type_node, scope, stack);
3578        }
3579
3580        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
3581            let range = Range {
3582                start_byte: node.start_byte(),
3583                end_byte: recovered.end_node.end_byte(),
3584                start_line: node.start_position().row + 1,
3585                end_line: recovered.end_node.end_position().row + 1,
3586            };
3587            let signature = self
3588                .source
3589                .get(range.start_byte..range.end_byte)
3590                .map(normalize_cpp_whitespace)
3591                .unwrap_or_default();
3592            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
3593            return;
3594        }
3595
3596        let alias_names = match node.kind() {
3597            "alias_declaration" => extract_alias_declaration_name(node, self.source)
3598                .into_iter()
3599                .collect::<Vec<_>>(),
3600            "type_definition" => extract_typedef_alias_names(node, self.source),
3601            _ => Vec::new(),
3602        };
3603        self.add_type_aliases(node, scope, alias_names);
3604    }
3605
3606    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
3607        let signature = normalize_cpp_whitespace(node_text(node, self.source));
3608        self.record_type_aliases(
3609            node,
3610            scope,
3611            alias_names,
3612            signature,
3613            cpp_declaration_range(node),
3614        );
3615    }
3616
3617    fn record_type_aliases(
3618        &mut self,
3619        node: Node<'_>,
3620        scope: &ScopeInfo,
3621        alias_names: Vec<String>,
3622        signature: String,
3623        range: Range,
3624    ) {
3625        if signature.is_empty() {
3626            return;
3627        }
3628        let type_name = node
3629            .child_by_field_name("type")
3630            .and_then(|type_node| type_node.child_by_field_name("name"))
3631            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
3632        for alias_name in alias_names {
3633            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
3634                continue;
3635            }
3636            let short_name = if let Some(parent) = &scope.class_unit {
3637                format!("{}${alias_name}", parent.short_name())
3638            } else {
3639                alias_name
3640            };
3641            let fq = cpp_class_fq(&scope.package_name, &short_name);
3642            let code_unit = CodeUnit::with_signature_and_fq(
3643                self.file.clone(),
3644                CodeUnitType::Class,
3645                scope.package_name.clone(),
3646                short_name,
3647                Some(signature.clone()),
3648                false,
3649                fq,
3650            );
3651            // Declaration identity does not include the alias signature. Keep
3652            // each physical range so conditional aliases retain their guards.
3653            self.parsed
3654                .add_code_unit_with_range(code_unit.clone(), range, None, None);
3655            self.parsed
3656                .add_signature(code_unit.clone(), signature.clone());
3657            if let Some(metadata) = &scope.template_metadata {
3658                let mut metadata = metadata.clone();
3659                metadata.primary_fq_name = code_unit.fq_name();
3660                self.parsed
3661                    .set_cpp_template_metadata(code_unit.clone(), metadata);
3662            }
3663            if let Some(parent) = &scope.class_unit {
3664                self.parsed.add_child(parent.clone(), code_unit.clone());
3665            } else if let Some(module) = &scope.module {
3666                self.parsed.add_child(module.clone(), code_unit.clone());
3667            }
3668            self.parsed.mark_type_alias(code_unit);
3669        }
3670    }
3671
3672    fn visit_macro(&mut self, node: Node<'_>) {
3673        let Some(name) = extract_macro_name(node, self.source) else {
3674            return;
3675        };
3676        let signature = node_text(node, self.source).trim_end().to_string();
3677        if signature.is_empty() {
3678            return;
3679        }
3680        let fq = cpp_member_fq("", &name);
3681        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
3682        if self.parsed.contains_declaration_identity(&code_unit) {
3683            return;
3684        }
3685        self.parsed
3686            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3687        let name_range = node
3688            .child_by_field_name("name")
3689            .map(cpp_declaration_range)
3690            .unwrap_or_else(|| cpp_declaration_range(node));
3691        self.parsed
3692            .record_materialization(MaterializationRecord::GeneratedDeclaration {
3693                site: cpp_declaration_range(node),
3694                argument: name_range,
3695                kind: GenerationKind::PreprocessorDefinition,
3696                unit: code_unit.clone(),
3697            });
3698        self.parsed.add_signature(code_unit, signature);
3699    }
3700}
3701
3702/// Classify a C++ field while its declaration syntax is already available.
3703///
3704/// The persisted result lets later visibility queries avoid reparsing the
3705/// complete source file only to recover linkage.
3706pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
3707    let mut current = declaration.parent();
3708    let mut enclosed_by_class = false;
3709    while let Some(node) = current {
3710        if node.kind() == "namespace_definition"
3711            && node
3712                .child_by_field_name("name")
3713                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3714        {
3715            return CppFieldLinkage::Internal;
3716        }
3717        if matches!(
3718            node.kind(),
3719            "class_specifier" | "struct_specifier" | "union_specifier"
3720        ) && node
3721            .child_by_field_name("name")
3722            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
3723        {
3724            return CppFieldLinkage::Internal;
3725        }
3726        if matches!(
3727            node.kind(),
3728            "class_specifier" | "struct_specifier" | "union_specifier"
3729        ) {
3730            enclosed_by_class = true;
3731        }
3732        if matches!(node.kind(), "function_definition" | "lambda_expression") {
3733            return CppFieldLinkage::Internal;
3734        }
3735        current = node.parent();
3736    }
3737    if enclosed_by_class {
3738        return CppFieldLinkage::External;
3739    }
3740    let mut cursor = declaration.walk();
3741    let mut has_static = false;
3742    let mut has_extern = false;
3743    let mut has_inline = false;
3744    let mut has_const = false;
3745    let mut has_constexpr = false;
3746    for child in declaration.named_children(&mut cursor) {
3747        let text = normalize_cpp_whitespace(node_text(child, source));
3748        match (child.kind(), text.as_str()) {
3749            ("storage_class_specifier", "static") => has_static = true,
3750            ("storage_class_specifier", "extern") => has_extern = true,
3751            ("storage_class_specifier", "inline") => has_inline = true,
3752            ("storage_class_specifier", "constexpr") => has_constexpr = true,
3753            ("type_qualifier", "const") => has_const = true,
3754            ("type_qualifier", "constexpr") => has_constexpr = true,
3755            _ => {}
3756        }
3757    }
3758    if has_static {
3759        CppFieldLinkage::Internal
3760    } else if has_extern || has_inline {
3761        CppFieldLinkage::External
3762    } else if has_const || has_constexpr {
3763        CppFieldLinkage::InternalUnlessExternalPeer
3764    } else {
3765        CppFieldLinkage::External
3766    }
3767}
3768
3769fn cpp_declaration_range(node: Node<'_>) -> Range {
3770    Range {
3771        start_byte: node.start_byte(),
3772        end_byte: node.end_byte(),
3773        start_line: node.start_position().row + 1,
3774        end_line: node.end_position().row + 1,
3775    }
3776}
3777
3778/// A recovery interval as a [`Range`], for materialization records whose
3779/// window is a byte region rather than one parser node (the sentinel-macro
3780/// region reparses, issue #941/#1657).
3781fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
3782    let line_at = |byte: usize| {
3783        source.as_bytes()[..byte]
3784            .iter()
3785            .filter(|&&b| b == b'\n')
3786            .count()
3787            + 1
3788    };
3789    Range {
3790        start_byte,
3791        end_byte,
3792        start_line: line_at(start_byte),
3793        end_line: line_at(end_byte),
3794    }
3795}
3796
3797pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
3798    let mut in_block_comment = false;
3799    for line in source.lines() {
3800        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
3801        let trimmed = stripped.trim();
3802        if !looks_like_quoted_include_line(trimmed) {
3803            continue;
3804        }
3805
3806        let raw = normalize_cpp_whitespace(trimmed);
3807        // The tree-sitter walk already recorded every `#include` it could see;
3808        // this line scan only recovers the ones a parse error hid, so skip a
3809        // snippet that is already an import binding.
3810        if parsed
3811            .imports
3812            .iter()
3813            .any(|import| import.raw_snippet == raw)
3814        {
3815            continue;
3816        }
3817
3818        parsed.imports.push(ImportInfo {
3819            raw_snippet: raw,
3820            is_wildcard: false,
3821            is_global: false,
3822            identifier: None,
3823            alias: None,
3824            path: None,
3825            binder_span: None,
3826        });
3827    }
3828}
3829
3830fn looks_like_quoted_include_line(line: &str) -> bool {
3831    let Some(rest) = line.trim_start().strip_prefix('#') else {
3832        return false;
3833    };
3834    let Some(rest) = rest.trim_start().strip_prefix("include") else {
3835        return false;
3836    };
3837    rest.trim_start().starts_with('"')
3838}
3839
3840fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
3841    let mut raw = Vec::new();
3842    let mut cursor = node.walk();
3843    for child in node.named_children(&mut cursor) {
3844        if child.kind() == "base_class_clause" {
3845            collect_cpp_base_nodes(child, source, &mut raw);
3846        }
3847    }
3848    raw
3849}
3850
3851fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
3852    walk_named_tree_preorder(node, false, |child| match child.kind() {
3853        "type_identifier" | "qualified_identifier" | "template_type" => {
3854            let text = normalize_cpp_whitespace(node_text(child, source));
3855            if !text.is_empty() {
3856                raw.push(text);
3857            }
3858            WalkControl::SkipChildren
3859        }
3860        _ => WalkControl::Continue,
3861    });
3862}
3863
3864fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
3865    let mut out = String::new();
3866    let chars: Vec<char> = line.chars().collect();
3867    let mut index = 0;
3868    let mut in_string = false;
3869    let mut in_char = false;
3870    let mut escape = false;
3871
3872    while index < chars.len() {
3873        let ch = chars[index];
3874        let next = chars.get(index + 1).copied();
3875
3876        if *in_block_comment {
3877            if ch == '*' && next == Some('/') {
3878                *in_block_comment = false;
3879                index += 2;
3880            } else {
3881                index += 1;
3882            }
3883            continue;
3884        }
3885
3886        if in_string {
3887            out.push(ch);
3888            if escape {
3889                escape = false;
3890            } else if ch == '\\' {
3891                escape = true;
3892            } else if ch == '"' {
3893                in_string = false;
3894            }
3895            index += 1;
3896            continue;
3897        }
3898
3899        if in_char {
3900            out.push(ch);
3901            if escape {
3902                escape = false;
3903            } else if ch == '\\' {
3904                escape = true;
3905            } else if ch == '\'' {
3906                in_char = false;
3907            }
3908            index += 1;
3909            continue;
3910        }
3911
3912        if ch == '/' && next == Some('/') {
3913            break;
3914        }
3915        if ch == '/' && next == Some('*') {
3916            *in_block_comment = true;
3917            index += 2;
3918            continue;
3919        }
3920        if ch == '"' {
3921            in_string = true;
3922            out.push(ch);
3923            index += 1;
3924            continue;
3925        }
3926        if ch == '\'' {
3927            in_char = true;
3928            out.push(ch);
3929            index += 1;
3930            continue;
3931        }
3932
3933        out.push(ch);
3934        index += 1;
3935    }
3936
3937    out
3938}
3939
3940#[derive(Clone)]
3941struct FunctionInfo {
3942    package_name: String,
3943    owner_path: Option<String>,
3944    name: String,
3945    signature: String,
3946}
3947
3948enum DeclaratorKind<'a> {
3949    Function(Node<'a>),
3950    Variable(Node<'a>),
3951}
3952
3953impl FunctionInfo {
3954    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
3955        self.code_unit_with_synthetic(file, false)
3956    }
3957
3958    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
3959        let short_name = if let Some(owner) = &self.owner_path {
3960            format!("{owner}.{}", self.name)
3961        } else {
3962            self.name.clone()
3963        };
3964        let fq = cpp_member_fq(&self.package_name, &short_name);
3965        CodeUnit::with_signature_and_fq(
3966            file,
3967            CodeUnitType::Function,
3968            self.package_name.clone(),
3969            short_name,
3970            Some(self.signature.clone()),
3971            synthetic,
3972            fq,
3973        )
3974    }
3975}
3976
3977fn extract_function_info(
3978    declarator: Node<'_>,
3979    source: &str,
3980    scope: &ScopeInfo,
3981) -> Option<FunctionInfo> {
3982    let parameters_node = declarator.child_by_field_name("parameters")?;
3983    let parameters_text = cpp_parameter_signature(parameters_node, source);
3984    let declarator_name_node = declarator
3985        .child_by_field_name("declarator")
3986        .or_else(|| parameters_node.prev_named_sibling())?;
3987    let recovered_specialization_member = scope
3988        .recovered_specialization_member_scope
3989        .then(|| {
3990            let terminal = declarator_name_node
3991                .child_by_field_name("name")
3992                .unwrap_or(declarator_name_node);
3993            let name = canonical_cpp_qualified_component(terminal, source)?.name;
3994            let owner = scope.class_unit.as_ref()?;
3995            Some((
3996                Some(owner.short_name().to_string()),
3997                name,
3998                scope.package_name.clone(),
3999            ))
4000        })
4001        .flatten();
4002    let (owner_path, name, package_name) = if let Some(parts) = recovered_specialization_member {
4003        parts
4004    } else if let Some(parts) =
4005        split_structured_templated_cpp_name(declarator_name_node, source, scope)
4006    {
4007        parts
4008    } else {
4009        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4010            declarator_name_node,
4011            source,
4012        )?);
4013        if raw_name.is_empty() {
4014            return None;
4015        }
4016        split_cpp_name(&raw_name, scope)
4017    };
4018    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4019    let mut signature = if suffix.is_empty() {
4020        parameters_text
4021    } else {
4022        format!("{parameters_text} {suffix}")
4023    };
4024    if let Some(template_signature) = &scope.template_signature {
4025        signature = format!("{template_signature}{signature}");
4026    }
4027
4028    Some(FunctionInfo {
4029        package_name,
4030        owner_path,
4031        name,
4032        signature,
4033    })
4034}
4035
4036/// The part of a `function_declarator` after its parameter list that belongs to
4037/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
4038/// specification, a trailing return type and a trailing requires-clause.
4039///
4040/// The grammar makes each of these a distinct sibling of the `parameters`
4041/// field, so they are read from the tree. Splitting the declarator's text on
4042/// the parameter list instead silently dropped every qualifier whenever the
4043/// parameter list was spelled with whitespace that normalization rewrote - a
4044/// line break or a double space was enough to make a `const` member definition
4045/// a different logical symbol from its declaration (#1827).
4046///
4047/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
4048/// are deliberately excluded. C++ does not make them part of the signature and
4049/// an out-of-line definition never repeats them, so including them would split
4050/// a declaration from its own definition.
4051fn cpp_declarator_identity_suffix(
4052    declarator: Node<'_>,
4053    parameters_node: Node<'_>,
4054    source: &str,
4055) -> String {
4056    let mut cursor = declarator.walk();
4057    let parts = declarator
4058        .named_children(&mut cursor)
4059        .filter(|child| child.start_byte() >= parameters_node.end_byte())
4060        .filter(|child| {
4061            matches!(
4062                child.kind(),
4063                "type_qualifier"
4064                    | "ref_qualifier"
4065                    | "noexcept"
4066                    | "throw_specifier"
4067                    | "trailing_return_type"
4068                    | "requires_clause"
4069            )
4070        })
4071        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4072        .filter(|text| !text.is_empty())
4073        .collect::<Vec<_>>();
4074    normalize_cpp_qualifier_suffix(&parts.join(" "))
4075}
4076
4077fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4078    match classify_declarator(node)? {
4079        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4080        DeclaratorKind::Variable(_) => None,
4081    }
4082}
4083
4084fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4085    match node.kind() {
4086        "function_declarator" => {
4087            let inner = node
4088                .child_by_field_name("declarator")
4089                .or_else(|| node.child_by_field_name("name"))
4090                .or_else(|| last_named_child(node));
4091            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4092                Some(DeclaratorKind::Variable(node))
4093            } else {
4094                Some(DeclaratorKind::Function(node))
4095            }
4096        }
4097        "init_declarator"
4098        | "pointer_declarator"
4099        | "reference_declarator"
4100        | "parenthesized_declarator"
4101        | "array_declarator"
4102        | "attributed_declarator"
4103        | "template_function" => node
4104            .child_by_field_name("declarator")
4105            .or_else(|| node.child_by_field_name("name"))
4106            .or_else(|| last_named_child(node))
4107            .and_then(classify_declarator),
4108        "identifier" | "field_identifier" | "qualified_identifier" => {
4109            Some(DeclaratorKind::Variable(node))
4110        }
4111        _ => node
4112            .child_by_field_name("declarator")
4113            .or_else(|| node.child_by_field_name("name"))
4114            .or_else(|| last_named_child(node))
4115            .and_then(classify_declarator),
4116    }
4117}
4118
4119fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4120    matches!(
4121        node.kind(),
4122        "function_declarator"
4123            | "init_declarator"
4124            | "pointer_declarator"
4125            | "reference_declarator"
4126            | "parenthesized_declarator"
4127            | "array_declarator"
4128            | "attributed_declarator"
4129            | "template_function"
4130            | "identifier"
4131            | "field_identifier"
4132            | "qualified_identifier"
4133    )
4134}
4135
4136fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4137    let class_like = first_class_like_child(node);
4138    let mut cursor = node.walk();
4139    node.named_children(&mut cursor).any(|child| {
4140        matches!(
4141            child.kind(),
4142            "init_declarator"
4143                | "pointer_declarator"
4144                | "reference_declarator"
4145                | "array_declarator"
4146                | "function_declarator"
4147                | "parenthesized_declarator"
4148                | "attributed_declarator"
4149        ) || matches!(
4150            child.kind(),
4151            "identifier" | "field_identifier" | "qualified_identifier"
4152        ) && class_like.is_none_or(|class_node| {
4153            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4154        })
4155    })
4156}
4157
4158/// Find the unique namespace-scope forward declaration that precedes a
4159/// recovered export-macro class definition.  Tree-sitter can close a malformed
4160/// class at the enclosing namespace's closing brace, leaving the later class
4161/// definitions as root-level recovered `function_definition` nodes.  A
4162/// preceding `class Name;` in the same namespace is the only structured identity
4163/// signal available in that shape.
4164///
4165/// The search is deliberately conservative: it only accepts a body-less class
4166/// specifier whose declaration has no declarator and is not nested in a function
4167/// or class body.  More than one matching namespace forward declaration is
4168/// ambiguous and returns `None` rather than guessing.
4169fn unique_earlier_cpp_namespace_forward(
4170    recovered_node: Node<'_>,
4171    name: &str,
4172    source: &str,
4173) -> Option<String> {
4174    let mut root = recovered_node;
4175    while let Some(parent) = root.parent() {
4176        root = parent;
4177    }
4178
4179    let mut candidates = Vec::new();
4180    let mut stack = vec![root];
4181    while let Some(current) = stack.pop() {
4182        if current.start_byte() < recovered_node.start_byte()
4183            && matches!(
4184                current.kind(),
4185                "class_specifier" | "struct_specifier" | "union_specifier"
4186            )
4187            && cpp_body_node(current).is_none()
4188            && current.parent().is_some_and(|parent| {
4189                parent.kind() == "declaration_list"
4190                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4191            })
4192            && class_like_name(current, source).as_deref() == Some(name)
4193            && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4194                // Borrowing is only justified by the parser-recovery shape we
4195                // are repairing: the namespace that held the forward must
4196                // itself contain a syntax error and must have closed before
4197                // the root-level recovered class. A clean, unrelated
4198                // namespace forward is not an identity proof.
4199                namespace.has_error()
4200                    && namespace.end_byte() < recovered_node.start_byte()
4201                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4202            })
4203            && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4204        {
4205            candidates.push(package_name);
4206        }
4207
4208        let mut cursor = current.walk();
4209        for child in current.named_children(&mut cursor) {
4210            if child.start_byte() < recovered_node.start_byte() {
4211                stack.push(child);
4212            }
4213        }
4214    }
4215
4216    if candidates.len() == 1 {
4217        candidates.pop()
4218    } else {
4219        None
4220    }
4221}
4222
4223fn malformed_namespace_is_nearest_recovery_region(
4224    namespace: Node<'_>,
4225    recovered_node: Node<'_>,
4226) -> bool {
4227    let mut root = recovered_node;
4228    while let Some(parent) = root.parent() {
4229        root = parent;
4230    }
4231    let mut cursor = root.walk();
4232    root.named_children(&mut cursor)
4233        .filter(|sibling| {
4234            namespace.end_byte() <= sibling.start_byte()
4235                && sibling.end_byte() <= recovered_node.start_byte()
4236        })
4237        .all(is_malformed_namespace_recovery_trivia)
4238}
4239
4240fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4241    matches!(node.kind(), "ERROR" | "comment")
4242        || node.kind().starts_with("preproc_")
4243        || node.kind() == "expression_statement" && node.named_child_count() == 0
4244}
4245
4246/// Return the namespace path for a forward class only when the declaration is
4247/// at namespace scope.  A declaration nested in a function/class body may share
4248/// the same namespace ancestor but cannot identify a top-level class definition.
4249fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4250    cpp_namespace_definition_for_forward(node)?;
4251    cpp_lexical_namespace_name(node, source)
4252}
4253
4254fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4255    let declaration = node.parent()?;
4256    let mut ancestor = declaration.parent();
4257    while let Some(current) = ancestor {
4258        if matches!(
4259            current.kind(),
4260            "compound_statement"
4261                | "field_declaration_list"
4262                | "class_specifier"
4263                | "struct_specifier"
4264                | "union_specifier"
4265                | "function_definition"
4266                | "lambda_expression"
4267        ) {
4268            return None;
4269        }
4270        if current.kind() == "namespace_definition" {
4271            return Some(current);
4272        }
4273        ancestor = current.parent();
4274    }
4275    None
4276}
4277
4278fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
4279    match node.kind() {
4280        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4281        "parenthesized_declarator" => node
4282            .child_by_field_name("declarator")
4283            .or_else(|| last_named_child(node))
4284            .is_some_and(is_pointer_wrapper_declarator),
4285        "template_function" => node
4286            .child_by_field_name("name")
4287            .is_some_and(is_function_pointer_like_inner_declarator),
4288        _ => false,
4289    }
4290}
4291
4292fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
4293    match node.kind() {
4294        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
4295        "parenthesized_declarator" => node
4296            .child_by_field_name("declarator")
4297            .or_else(|| last_named_child(node))
4298            .is_some_and(is_pointer_wrapper_declarator),
4299        _ => false,
4300    }
4301}
4302
4303fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<String>, String, String) {
4304    let cleaned = raw_name.trim_start_matches("template ").trim();
4305    // A leading `::` is the explicit-global marker, not an empty owner segment.
4306    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
4307    // erroneous macro envelope swallowing the first identifier of an
4308    // out-of-line `X::X` constructor, chromium #1573); without this strip the
4309    // split below yields owner_parts `[""]`, constructing a unit with an empty
4310    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
4311    let cleaned = cleaned.trim_start_matches("::");
4312    // Parser recovery can preserve two adjacent scope operators around a
4313    // missing component (for example `X::/**/::method` in compiler diagnostic
4314    // fixtures). Empty components are syntax-recovery artifacts, never C++
4315    // owners. Keeping one as the final owner constructed `short_name=".method"`
4316    // and violated the structured package/short boundary during a large LLVM
4317    // workspace build. This is the same legacy-string-to-FqName bridge as the
4318    // ordinary split above; discard only components that the delimiter itself
4319    // proves empty.
4320    let parts: Vec<_> = cleaned
4321        .split("::")
4322        .filter(|component| !component.is_empty())
4323        .collect();
4324    if parts.is_empty() {
4325        return (None, cleaned.to_string(), scope.package_name.clone());
4326    }
4327    if parts.len() > 1 {
4328        let name = parts.last().unwrap_or(&cleaned).to_string();
4329        let owner_parts = &parts[..parts.len() - 1];
4330        if let Some(class_unit) = &scope.class_unit {
4331            // Lexically inside a class body: the owner is that class, whatever
4332            // the declarator re-qualifies it as.
4333            return (
4334                Some(class_unit.short_name().to_string()),
4335                name,
4336                scope.package_name.clone(),
4337            );
4338        }
4339        if !scope.package_name.is_empty() {
4340            // Out-of-line member definition written *inside* an enclosing
4341            // `namespace {}` block (scope package is that namespace). Every
4342            // owner segment before the terminal member is a class-nesting step
4343            // -- an out-of-line nested-class member `Outer::Inner::method` in
4344            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
4345            // namespace path: `using namespace` never brings nested-class
4346            // access into unqualified scope, so C++ always writes the full
4347            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
4348            // that redundantly re-states the enclosing namespace it already
4349            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
4350            // strip that re-qualifying prefix (which duplicates a suffix of the
4351            // enclosing package path) before treating what remains as the
4352            // nested-class chain, so the redundant spelling still lands on the
4353            // same `log4cxx.Foo.method` identity as its header declaration.
4354            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
4355            let owner_path = (!nested.is_empty()).then(|| nested.join("$"));
4356            return (owner_path, name, scope.package_name.clone());
4357        }
4358        // File scope (no enclosing `namespace {}` block, scope package empty).
4359        let (owner_path, package_name) = if owner_parts.len() > 1 {
4360            // A multi-segment qualifier at file scope with no enclosing
4361            // namespace: treat all but the last owner segment as the namespace
4362            // path and the last as the owning class (`ns1::ns2::Class::method`
4363            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
4364            // is really a namespace or an outer class cannot be told from the
4365            // declarator text alone here, and no enclosing namespace or
4366            // in-index owner is available at per-file extraction to confirm the
4367            // class reading, so the far-more-common namespace interpretation is
4368            // kept rather than guessed away (the nested-class-at-file-scope and
4369            // using-directive-qualified nested-class shapes remain on this
4370            // behavior; see #1121).
4371            (
4372                Some(owner_parts.last().unwrap_or(&"").to_string()),
4373                owner_parts[..owner_parts.len() - 1].join("::"),
4374            )
4375        } else {
4376            // A bare `Class::member` qualifier at file scope carries no
4377            // namespace segment of its own. The declarator alone cannot say
4378            // which namespace owns `Class` -- but a `using namespace X;`
4379            // directive already in effect at this point in the file (#1093,
4380            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
4381            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
4382            // is the remaining structural signal for it, so fall back to it
4383            // rather than leaving the definition's package empty while its
4384            // header declaration (parsed inside the `namespace {}` block) keeps
4385            // the real one -- an identity split that made the same member
4386            // unresolvable under its own displayed spelling.
4387            (
4388                Some(owner_parts[0].to_string()),
4389                cpp_using_directive_namespace_for_bare_owner(scope),
4390            )
4391        };
4392        return (owner_path, name, package_name);
4393    }
4394
4395    let package_name = scope.package_name.clone();
4396    let owner_path = scope
4397        .class_unit
4398        .as_ref()
4399        .map(|parent| parent.short_name().to_string());
4400    (owner_path, cleaned.to_string(), package_name)
4401}
4402
4403/// Drop the leading owner segments of an out-of-line member qualifier that
4404/// merely re-state the enclosing namespace the definition already sits in, so
4405/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
4406/// definition may redundantly write `a::b::Outer::Inner::method` (or the
4407/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
4408/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
4409/// noise, not class-nesting steps. Returns the owner segments with the longest
4410/// such re-qualifying prefix removed (possibly all of them, when the qualifier
4411/// names only the enclosing namespace before the terminal member -- a
4412/// re-qualified free function). `package_name` is the enclosing namespace path
4413/// in its stored `::`-joined form; both sides are split on the same delimiter
4414/// the namespace walker joined them with, so this compares namespace *segments*
4415/// rather than scanning text.
4416fn strip_redundant_namespace_prefix<'a>(
4417    owner_parts: &'a [&'a str],
4418    package_name: &str,
4419) -> &'a [&'a str] {
4420    if package_name.is_empty() {
4421        return owner_parts;
4422    }
4423    let package_segments: Vec<&str> = package_name.split("::").collect();
4424    let max_prefix = owner_parts.len().min(package_segments.len());
4425    for prefix_len in (1..=max_prefix).rev() {
4426        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
4427        if &owner_parts[..prefix_len] == package_suffix {
4428            return &owner_parts[prefix_len..];
4429        }
4430    }
4431    owner_parts
4432}
4433
4434/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
4435/// class name at file/namespace scope, from the `using namespace` directives
4436/// visible at this point in the file. Several may be in scope at once (a
4437/// primary `using namespace NS;` alongside deeper conveniences like `using
4438/// namespace NS::helpers;`); since the declarator gives no way to tell which
4439/// one actually declares the owner class, prefer the shallowest (fewest
4440/// `::`-separated segments) as the file's most likely "home" namespace,
4441/// breaking ties by declaration order. Returns an empty string (leaving the
4442/// caller's package unqualified, as before) when no using-namespace directive
4443/// is in scope.
4444fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
4445    scope
4446        .visible_using_namespaces
4447        .iter()
4448        .min_by_key(|namespace| namespace.split("::").count())
4449        .cloned()
4450        .unwrap_or_default()
4451}
4452
4453struct CppQualifiedNameComponent {
4454    name: String,
4455    is_template_id: bool,
4456}
4457
4458fn split_structured_templated_cpp_name(
4459    declarator_name: Node<'_>,
4460    source: &str,
4461    scope: &ScopeInfo,
4462) -> Option<(Option<String>, String, String)> {
4463    if declarator_name.kind() != "qualified_identifier" {
4464        return None;
4465    }
4466
4467    let mut components = Vec::new();
4468    let mut current = declarator_name;
4469    let mut explicitly_global = false;
4470    loop {
4471        if current.kind() == "qualified_identifier" {
4472            if let Some(component) = current.child_by_field_name("scope") {
4473                components.push(canonical_cpp_qualified_component(component, source)?);
4474            } else if components.is_empty() {
4475                explicitly_global = true;
4476            } else {
4477                return None;
4478            }
4479            current = current.child_by_field_name("name")?;
4480        } else {
4481            components.push(canonical_cpp_qualified_component(current, source)?);
4482            break;
4483        }
4484    }
4485
4486    let terminal = components.pop()?;
4487    let owner_start = components
4488        .iter()
4489        .position(|component| component.is_template_id)?;
4490    let explicit_package = components[..owner_start]
4491        .iter()
4492        .map(|component| component.name.as_str())
4493        .collect::<Vec<_>>()
4494        .join("::");
4495    let explicit_package_is_empty = explicit_package.is_empty();
4496    let package_name = match (
4497        explicitly_global,
4498        scope.package_name.is_empty(),
4499        explicit_package_is_empty,
4500    ) {
4501        (true, _, _) => explicit_package,
4502        (false, _, true) => scope.package_name.clone(),
4503        (false, true, false) => explicit_package,
4504        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
4505    };
4506    // Same identity-split fallback as `split_cpp_name` (#1093): a template
4507    // specialization's owner class named with no namespace segment of its own
4508    // (`explicit_package` empty) at file scope (`explicitly_global` false)
4509    // with nothing enclosing (`package_name` still empty) has no structural
4510    // signal for its namespace besides an in-scope `using namespace X;`.
4511    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
4512    {
4513        cpp_using_directive_namespace_for_bare_owner(scope)
4514    } else {
4515        package_name
4516    };
4517    let owner_path = components[owner_start..]
4518        .iter()
4519        .map(|component| component.name.as_str())
4520        .collect::<Vec<_>>()
4521        .join("$");
4522    if owner_path.is_empty() || terminal.name.is_empty() {
4523        return None;
4524    }
4525
4526    Some((Some(owner_path), terminal.name, package_name))
4527}
4528
4529fn canonical_cpp_qualified_component(
4530    mut component: Node<'_>,
4531    source: &str,
4532) -> Option<CppQualifiedNameComponent> {
4533    let mut is_template_id = false;
4534    loop {
4535        match component.kind() {
4536            "template_type" => {
4537                is_template_id = true;
4538                component = component.child_by_field_name("name")?;
4539            }
4540            "dependent_name" => component = component.named_child(0)?,
4541            "identifier"
4542            | "field_identifier"
4543            | "namespace_identifier"
4544            | "type_identifier"
4545            | "operator_name"
4546            | "destructor_name" => {
4547                let name = normalize_cpp_whitespace(node_text(component, source));
4548                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
4549                    name,
4550                    is_template_id,
4551                });
4552            }
4553            _ => component = component.child_by_field_name("name")?,
4554        }
4555    }
4556}
4557
4558fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
4559    match node.kind() {
4560        "identifier"
4561        | "field_identifier"
4562        | "type_identifier"
4563        | "operator_name"
4564        | "destructor_name"
4565        | "qualified_identifier" => node_text(node, source).to_string(),
4566        "function_declarator"
4567        | "pointer_declarator"
4568        | "reference_declarator"
4569        | "parenthesized_declarator"
4570        | "array_declarator"
4571        | "template_function" => node
4572            .child_by_field_name("declarator")
4573            .or_else(|| node.child_by_field_name("name"))
4574            .or_else(|| last_named_child(node))
4575            .map(|child| extract_declarator_name(child, source))
4576            .unwrap_or_else(|| node_text(node, source).to_string()),
4577        _ => node
4578            .child_by_field_name("name")
4579            .map(|child| extract_declarator_name(child, source))
4580            .unwrap_or_else(|| node_text(node, source).to_string()),
4581    }
4582}
4583
4584/// Extract a callable identity only through declaration-shaped AST nodes.
4585/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
4586/// expose the call's parameter list as a false function declarator; accepting
4587/// arbitrary node text there emitted bogus names such as `.*f`.
4588fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4589    match node.kind() {
4590        "identifier"
4591        | "field_identifier"
4592        | "type_identifier"
4593        | "operator_name"
4594        | "destructor_name"
4595        | "qualified_identifier" => Some(node_text(node, source).to_string()),
4596        "function_declarator"
4597        | "pointer_declarator"
4598        | "reference_declarator"
4599        | "parenthesized_declarator"
4600        | "array_declarator"
4601        | "template_function" => node
4602            .child_by_field_name("declarator")
4603            .or_else(|| node.child_by_field_name("name"))
4604            .and_then(|child| extract_callable_declarator_name(child, source)),
4605        _ => None,
4606    }
4607}
4608
4609fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
4610    match node.kind() {
4611        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
4612            let name = node_text(node, source).trim().to_string();
4613            (!name.is_empty()).then_some(name)
4614        }
4615        _ => node
4616            .child_by_field_name("declarator")
4617            .or_else(|| node.child_by_field_name("name"))
4618            .or_else(|| last_named_child(node))
4619            .and_then(|child| extract_variable_name(child, source)),
4620    }
4621}
4622
4623fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
4624    let count = node.named_child_count();
4625    if count == 0 {
4626        None
4627    } else {
4628        node.named_child(count - 1)
4629    }
4630}
4631
4632fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
4633    let name_node = node.child_by_field_name("name")?;
4634    let name = normalize_cpp_whitespace(node_text(name_node, source));
4635    (!name.is_empty()).then_some(name)
4636}
4637
4638fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4639    if node.kind() != "declaration" {
4640        return Vec::new();
4641    }
4642    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
4643        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
4644    }) else {
4645        return Vec::new();
4646    };
4647    let Some(declarator) = node.child_by_field_name("declarator") else {
4648        return Vec::new();
4649    };
4650    if node_text(keyword, source) == "using"
4651        && (declarator.kind() != "init_declarator"
4652            || declarator.child_by_field_name("value").is_none())
4653    {
4654        return Vec::new();
4655    }
4656    if node_text(keyword, source) == "typedef"
4657        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
4658    {
4659        return vec![alias_name];
4660    }
4661    extract_typedef_declarator_name(declarator, source)
4662        .into_iter()
4663        .collect()
4664}
4665
4666fn recovered_typedef_error_alias_name(
4667    declaration: Node<'_>,
4668    declarator: Node<'_>,
4669    source: &str,
4670) -> Option<String> {
4671    // An export macro between `class` and its name can make tree-sitter parse
4672    // the recovered class body as a function body. In that shape,
4673    //
4674    //     typedef spi::Filter BASE_CLASS;
4675    //
4676    // becomes a declaration whose `declarator` is the underlying qualified
4677    // type (`spi::Filter`) and whose actual alias name is displaced into the
4678    // following ERROR node. Do not publish the terminal underlying type
4679    // (`Filter`) as a false class-owned alias.
4680    if declarator.kind() != "qualified_identifier" {
4681        return None;
4682    }
4683    let mut cursor = declaration.walk();
4684    let mut errors = declaration
4685        .named_children(&mut cursor)
4686        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
4687    let error = errors.next()?;
4688    if errors.next().is_some() || error.named_child_count() != 1 {
4689        return None;
4690    }
4691    let name = error.named_child(0)?;
4692    if !matches!(
4693        name.kind(),
4694        "identifier" | "field_identifier" | "type_identifier"
4695    ) {
4696        return None;
4697    }
4698    let name = normalize_cpp_whitespace(node_text(name, source));
4699    (!name.is_empty()).then_some(name)
4700}
4701
4702fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
4703    // A function-like token in the type position can make tree-sitter expose
4704    // its argument as a parenthesized declarator. Do not publish that argument
4705    // as an alias. The macro-specific recovery below handles the proven shape.
4706    if fragmented_parenthesized_typedef_type(node).is_some() {
4707        return Vec::new();
4708    }
4709    let has_function_like_macro_type = node
4710        .child_by_field_name("type")
4711        .filter(|type_node| type_node.kind() == "type_identifier")
4712        .is_some_and(|type_node| {
4713            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4714        });
4715    let mut names = Vec::new();
4716    let mut cursor = node.walk();
4717    for declarator in node.children_by_field_name("declarator", &mut cursor) {
4718        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
4719            continue;
4720        }
4721        if let Some(name) = extract_typedef_declarator_name(declarator, source)
4722            && !names.contains(&name)
4723        {
4724            names.push(name);
4725        }
4726    }
4727    names
4728}
4729
4730struct RecoveredMacroTypedefAlias<'tree> {
4731    name: String,
4732    end_node: Node<'tree>,
4733}
4734
4735/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
4736/// into an identifier expression statement. The uppercase macro token, missing
4737/// typedef terminator, and complete sibling terminator prove this exact shape.
4738fn recovered_macro_typedef_alias<'tree>(
4739    node: Node<'tree>,
4740    source: &str,
4741) -> Option<RecoveredMacroTypedefAlias<'tree>> {
4742    let type_node = fragmented_parenthesized_typedef_type(node)?;
4743    if type_node.kind() != "type_identifier"
4744        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
4745    {
4746        return None;
4747    }
4748
4749    let end_node = node.next_named_sibling()?;
4750    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
4751        return None;
4752    }
4753    let name_node = end_node.named_child(0)?;
4754    if name_node.kind() != "identifier" {
4755        return None;
4756    }
4757    let has_terminator = (0..end_node.child_count()).any(|index| {
4758        end_node
4759            .child(index)
4760            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
4761    });
4762    if !has_terminator {
4763        return None;
4764    }
4765    let name = normalize_cpp_whitespace(node_text(name_node, source));
4766    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
4767}
4768
4769fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
4770    if node.kind() != "type_definition" {
4771        return None;
4772    }
4773    let mut declarator_cursor = node.walk();
4774    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
4775    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
4776        return None;
4777    }
4778    let has_missing_terminator = (0..node.child_count()).any(|index| {
4779        node.child(index)
4780            .is_some_and(|child| child.kind() == ";" && child.is_missing())
4781    });
4782    if !has_missing_terminator {
4783        return None;
4784    }
4785    node.child_by_field_name("type")
4786}
4787
4788fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
4789    match node.kind() {
4790        "identifier" | "field_identifier" | "type_identifier" => {
4791            let name = normalize_cpp_whitespace(node_text(node, source));
4792            (!name.is_empty()).then_some(name)
4793        }
4794        "qualified_identifier" => node
4795            .child_by_field_name("name")
4796            .and_then(|name| extract_typedef_declarator_name(name, source)),
4797        _ => node
4798            .child_by_field_name("declarator")
4799            .or_else(|| node.child_by_field_name("name"))
4800            .or_else(|| last_named_child(node))
4801            .and_then(|child| extract_typedef_declarator_name(child, source)),
4802    }
4803}
4804
4805fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
4806    let name = node
4807        .child_by_field_name("name")
4808        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4809        .or_else(|| {
4810            let mut cursor = node.walk();
4811            node.named_children(&mut cursor)
4812                .find(|child| {
4813                    matches!(
4814                        child.kind(),
4815                        "identifier" | "field_identifier" | "type_identifier"
4816                    )
4817                })
4818                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
4819        })?;
4820    (!name.is_empty()).then_some(name)
4821}
4822
4823fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
4824    left.id() == right.id()
4825}
4826
4827fn render_cpp_type_signature(
4828    node: Node<'_>,
4829    source: &str,
4830    template_signature: Option<&str>,
4831) -> String {
4832    let text = normalize_cpp_whitespace(node_text(node, source));
4833    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
4834    let rendered = if head.ends_with(';') {
4835        head.to_string()
4836    } else {
4837        format!("{head} {{")
4838    };
4839    if let Some(template_signature) = template_signature {
4840        format!("template {template_signature} {rendered}")
4841    } else {
4842        rendered
4843    }
4844}
4845
4846fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
4847    if let Some(signature) =
4848        render_recovered_macro_qualified_field_signature(node, declarator, source)
4849    {
4850        return signature;
4851    }
4852    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
4853    let prefix = cpp_declaration_prefix(node, source);
4854    let name = extract_variable_name(declarator, source).unwrap_or_default();
4855    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
4856    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
4857        || (prefix.ends_with('&') && raw_suffix == "&")
4858    {
4859        String::new()
4860    } else {
4861        raw_suffix
4862    };
4863
4864    let mut rendered = if suffix.is_empty() {
4865        format!("{prefix} {name}")
4866    } else if suffix.starts_with('*') || suffix.starts_with('&') {
4867        format!("{prefix}{suffix} {name}")
4868    } else if suffix.starts_with('[') || suffix.starts_with('(') {
4869        format!("{prefix} {name}{suffix}")
4870    } else {
4871        format!("{prefix} {suffix}{name}")
4872    };
4873    rendered = collapse_cpp_whitespace(&rendered);
4874
4875    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4876        format!("{rendered} = {initializer};")
4877    } else if declaration_text.ends_with(';') {
4878        format!("{rendered};")
4879    } else {
4880        rendered
4881    }
4882}
4883
4884fn render_recovered_macro_qualified_field_signature(
4885    node: Node<'_>,
4886    declarator: Node<'_>,
4887    source: &str,
4888) -> Option<String> {
4889    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
4890    if !recovered
4891        .iter()
4892        .any(|candidate| same_node(*candidate, declarator))
4893    {
4894        return None;
4895    }
4896    let pseudo_declarator = node.child_by_field_name("declarator")?;
4897    let mut cursor = node.walk();
4898    let clause = node
4899        .named_children(&mut cursor)
4900        .find(|child| child.kind() == "bitfield_clause")?;
4901    let mut cursor = clause.walk();
4902    let error = clause
4903        .named_children(&mut cursor)
4904        .find(|child| child.kind() == "ERROR")?;
4905    let qualified_type =
4906        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
4907    let prefix = cpp_declaration_prefix(node, source);
4908    let name = extract_variable_name(declarator, source)?;
4909    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
4910    let mut rendered = if suffix.is_empty() {
4911        format!("{prefix} {qualified_type} {name}")
4912    } else {
4913        format!("{prefix} {qualified_type} {suffix} {name}")
4914    };
4915    rendered = collapse_cpp_whitespace(&rendered);
4916
4917    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
4918        Some(format!(
4919            "{rendered} = {};",
4920            normalize_cpp_whitespace(node_text(initializer, source))
4921        ))
4922    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
4923        Some(format!("{rendered} = {initializer};"))
4924    } else {
4925        Some(format!("{rendered};"))
4926    }
4927}
4928
4929fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
4930    match node.kind() {
4931        "pointer_expression" => {
4932            let operator = node
4933                .child_by_field_name("operator")
4934                .or_else(|| node.child(0))
4935                .map(|operator| node_text(operator, source))
4936                .unwrap_or("*");
4937            let argument = node
4938                .child_by_field_name("argument")
4939                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4940                .unwrap_or_default();
4941            format!("{operator}{argument}")
4942        }
4943        "unary_expression" => {
4944            let operator = node
4945                .child_by_field_name("operator")
4946                .or_else(|| node.child(0))
4947                .map(|operator| node_text(operator, source))
4948                .unwrap_or_default();
4949            let argument = node
4950                .child_by_field_name("argument")
4951                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
4952                .unwrap_or_default();
4953            format!("{operator}{argument}")
4954        }
4955        "identifier" | "field_identifier" => String::new(),
4956        _ => cpp_declarator_suffix_without_name(node, source),
4957    }
4958}
4959
4960fn recovered_macro_qualified_field_initializer<'tree>(
4961    clause: Node<'tree>,
4962    declarator: Node<'tree>,
4963) -> Option<Node<'tree>> {
4964    let mut stack = vec![clause];
4965    while let Some(current) = stack.pop() {
4966        if current.kind() == "assignment_expression"
4967            && current
4968                .child_by_field_name("left")
4969                .is_some_and(|left| same_node(left, declarator))
4970        {
4971            return current.child_by_field_name("right");
4972        }
4973        let mut cursor = current.walk();
4974        stack.extend(current.named_children(&mut cursor));
4975    }
4976    None
4977}
4978
4979fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
4980    let text = node_text(node, source);
4981    let mut cursor = node.walk();
4982    let first_declarator = node.named_children(&mut cursor).find(|child| {
4983        matches!(
4984            child.kind(),
4985            "init_declarator"
4986                | "identifier"
4987                | "field_identifier"
4988                | "pointer_declarator"
4989                | "reference_declarator"
4990                | "array_declarator"
4991                | "function_declarator"
4992        )
4993    });
4994    let prefix = if let Some(first_declarator) = first_declarator {
4995        let end = first_declarator
4996            .start_byte()
4997            .saturating_sub(node.start_byte());
4998        let mut prefix = text.get(..end).unwrap_or(text).to_string();
4999        let declarator_suffix = match first_declarator.kind() {
5000            "init_declarator" => first_declarator
5001                .child_by_field_name("declarator")
5002                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5003                .unwrap_or_default(),
5004            _ => cpp_declarator_suffix_without_name(first_declarator, source),
5005        };
5006        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5007            prefix.push_str(&declarator_suffix);
5008        }
5009        return collapse_cpp_whitespace(&prefix)
5010            .trim_end_matches(',')
5011            .trim_end_matches(';')
5012            .trim()
5013            .to_string();
5014    } else {
5015        text
5016    };
5017    collapse_cpp_whitespace(prefix)
5018        .trim_end_matches(',')
5019        .trim_end_matches(';')
5020        .trim()
5021        .to_string()
5022}
5023
5024fn cpp_preserved_initializer(
5025    declaration_node: Node<'_>,
5026    declarator: Node<'_>,
5027    source: &str,
5028) -> Option<String> {
5029    let name = extract_variable_name(declarator, source)?;
5030    let mut cursor = declaration_node.walk();
5031    for child in declaration_node.named_children(&mut cursor) {
5032        if child.kind() != "init_declarator" {
5033            continue;
5034        }
5035        let Some(inner) = child.child_by_field_name("declarator") else {
5036            continue;
5037        };
5038        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5039            continue;
5040        }
5041        let value = child.child_by_field_name("value")?;
5042        let kind = value.kind();
5043        if matches!(
5044            kind,
5045            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5046        ) {
5047            return Some(normalize_cpp_whitespace(node_text(value, source)));
5048        }
5049        break;
5050    }
5051    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5052    let pattern = format!(
5053        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5054        regex::escape(&name)
5055    );
5056    Regex::new(&pattern)
5057        .ok()
5058        .and_then(|regex| regex.captures(&declaration_text))
5059        .and_then(|captures| captures.get(1))
5060        .map(|value| value.as_str().to_string())
5061}
5062
5063fn render_cpp_function_display_signature_from_node(
5064    node: Node<'_>,
5065    source: &str,
5066    template_signature: Option<&str>,
5067    has_body: bool,
5068) -> String {
5069    let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5070    let parent_text = node_text(root, source);
5071    let body_local_start = root
5072        .child_by_field_name("body")
5073        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5074        .unwrap_or(parent_text.len());
5075    let display = parent_text
5076        .get(..body_local_start)
5077        .unwrap_or(parent_text)
5078        .trim()
5079        .trim();
5080    let display = if let Some(template_signature) = template_signature {
5081        if display.starts_with("template ") {
5082            display.to_string()
5083        } else {
5084            format!("template {template_signature} {display}")
5085        }
5086    } else {
5087        display.to_string()
5088    };
5089    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5090    if has_body {
5091        format!("{display} {{...}}")
5092    } else {
5093        format!("{display};")
5094    }
5095}
5096
5097fn cpp_template_signature(
5098    template_node: Node<'_>,
5099    declaration_child: Node<'_>,
5100    source: &str,
5101) -> Option<String> {
5102    let text = source
5103        .get(template_node.start_byte()..declaration_child.start_byte())
5104        .unwrap_or("");
5105    let text = normalize_cpp_whitespace(text);
5106    let start = text.find('<')?;
5107    let end = text.rfind('>')?;
5108    if end < start {
5109        return None;
5110    }
5111    Some(text[start..=end].to_string())
5112}
5113
5114struct RecoveredFragmentedPartialSpecialization<'tree> {
5115    declaration_node: Node<'tree>,
5116    name: String,
5117    range: Range,
5118    prefix_members: Vec<Node<'tree>>,
5119    member_siblings: Vec<Node<'tree>>,
5120    following_declarations: Vec<Node<'tree>>,
5121}
5122
5123struct RecoveredFragmentedPreprocessorClass<'tree> {
5124    declaration_node: Node<'tree>,
5125    class_node: Node<'tree>,
5126    body: Node<'tree>,
5127    name: String,
5128    range: Range,
5129    tail_members: Vec<Node<'tree>>,
5130    member_siblings: Vec<Node<'tree>>,
5131}
5132
5133/// Recover a class whose preprocessor-fragmented parse closes at an early
5134/// member body and publishes the remaining in-class declarations as siblings
5135/// of the surrounding alternative. Primary classes are admitted only when an
5136/// earlier branch contains the matching bodyless declaration and the class
5137/// node retains the displaced `#endif`. Partial specializations instead carry
5138/// their identity structurally in the `template_type` name and template
5139/// metadata. Retain the original AST nodes and re-own only the siblings through
5140/// the displaced structural `};` terminator.
5141fn recover_fragmented_preprocessor_class<'tree>(
5142    template_node: Node<'tree>,
5143    source: &str,
5144) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5145    let alternative = template_node.parent()?;
5146    if alternative.kind() != "preproc_else" {
5147        return None;
5148    }
5149    let conditional = alternative.parent()?;
5150    if conditional.kind() != "preproc_if" {
5151        return None;
5152    }
5153    let declaration_node = template_node
5154        .named_children(&mut template_node.walk())
5155        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5156    let class_node = declaration_node
5157        .named_children(&mut declaration_node.walk())
5158        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5159    let body = cpp_body_node(class_node)?;
5160    if class_node.end_byte() >= declaration_node.end_byte() {
5161        return None;
5162    }
5163    let name = class_like_name(class_node, source)?;
5164    let is_partial_specialization = class_node
5165        .child_by_field_name("name")
5166        .is_some_and(|class_name| class_name.kind() == "template_type");
5167    if is_partial_specialization {
5168        let metadata = cpp_template_metadata(template_node, class_node, source)?;
5169        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5170            return None;
5171        }
5172    } else {
5173        if !class_has_displaced_preprocessor_terminator(class_node) {
5174            return None;
5175        }
5176        let matching_other_branch = conditional
5177            .named_children(&mut conditional.walk())
5178            .take_while(|child| !same_node(*child, alternative))
5179            .filter(|child| child.kind() == "template_declaration")
5180            .filter_map(first_class_like_child)
5181            .any(|candidate| {
5182                cpp_body_node(candidate).is_none()
5183                    && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5184            });
5185        if !matching_other_branch {
5186            return None;
5187        }
5188    }
5189
5190    let mut tail_members = Vec::new();
5191    let mut saw_class = false;
5192    let mut declaration_cursor = declaration_node.walk();
5193    for child in declaration_node.named_children(&mut declaration_cursor) {
5194        if same_node(child, class_node) {
5195            saw_class = true;
5196        } else if saw_class {
5197            tail_members.push(child);
5198        }
5199    }
5200
5201    let mut member_siblings = Vec::new();
5202    let mut saw_template = false;
5203    let mut terminator = None;
5204    for index in 0..alternative.child_count() {
5205        let Some(child) = alternative.child(index) else {
5206            continue;
5207        };
5208        if same_node(child, template_node) {
5209            saw_template = true;
5210            continue;
5211        }
5212        if !saw_template {
5213            continue;
5214        }
5215        if displaced_fragmented_class_terminator(alternative, index) {
5216            terminator = alternative.child(index + 1);
5217            break;
5218        }
5219        if child.is_named() {
5220            member_siblings.push(child);
5221        }
5222    }
5223    let terminator = terminator?;
5224    Some(RecoveredFragmentedPreprocessorClass {
5225        declaration_node,
5226        class_node,
5227        body,
5228        name,
5229        range: Range {
5230            start_byte: class_node.start_byte(),
5231            end_byte: terminator.end_byte(),
5232            start_line: class_node.start_position().row + 1,
5233            end_line: terminator.end_position().row + 1,
5234        },
5235        tail_members,
5236        member_siblings,
5237    })
5238}
5239
5240fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
5241    (0..class_node.child_count()).any(|index| {
5242        class_node.child(index).is_some_and(|child| {
5243            child.kind() == "ERROR"
5244                && (0..child.child_count()).any(|error_index| {
5245                    child
5246                        .child(error_index)
5247                        .is_some_and(|token| token.kind() == "#endif")
5248                })
5249        })
5250    })
5251}
5252
5253/// The real `#endif` that tree-sitter consumed inside an error subtree.
5254///
5255/// A preprocessor directive inside a malformed array bound can cause later
5256/// declarations to remain children of the conditional. The non-missing token
5257/// still gives the exact structured boundary. Ignore nested conditionals and
5258/// select the last error-owned token. Tree-sitter can pair a later outer
5259/// `#endif` with this conditional, so the direct terminator is not necessarily
5260/// missing.
5261pub fn cpp_displaced_preprocessor_terminator<'tree>(
5262    conditional: Node<'tree>,
5263) -> Option<Node<'tree>> {
5264    if !conditional.has_error() {
5265        return None;
5266    }
5267    let has_concrete_direct_terminator = conditional
5268        .child_count()
5269        .checked_sub(1)
5270        .and_then(|index| conditional.child(index))
5271        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
5272    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
5273        // A structured alternative proves that the direct `#endif` closes
5274        // this family. An error-owned terminator inside either branch belongs
5275        // to a damaged nested conditional, not to this one.
5276        return None;
5277    }
5278    let mut displaced = None;
5279    let mut stack = (0..conditional.child_count())
5280        .filter_map(|index| conditional.child(index))
5281        .map(|child| (child, false))
5282        .collect::<Vec<_>>();
5283    while let Some((node, inside_error)) = stack.pop() {
5284        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
5285            continue;
5286        }
5287        if node.kind() == "#endif" && !node.is_missing() && inside_error {
5288            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
5289                displaced = Some(node);
5290            }
5291            continue;
5292        }
5293        if node != conditional
5294            && matches!(
5295                node.kind(),
5296                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
5297            )
5298        {
5299            continue;
5300        }
5301        let inside_error = inside_error || node.kind() == "ERROR";
5302        for index in 0..node.child_count() {
5303            if let Some(child) = node.child(index) {
5304                stack.push((child, inside_error));
5305            }
5306        }
5307    }
5308    displaced
5309}
5310
5311/// The effective end of a conditional whose real terminator tree-sitter
5312/// displaced into declaration recovery.
5313///
5314/// Most damaged conditionals retain a concrete `#endif` token below an
5315/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
5316/// boundary. A preprocessor family that selects the middle of a declaration
5317/// can lose the directive tokens entirely. In that shape tree-sitter leaves
5318/// the declaration's `typedef` token as the sole child of the immediately
5319/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
5320/// declarator name inside the conditional's first declaration. The declaration
5321/// end is then the smallest structured boundary that contains the whole split
5322/// declaration.
5323#[derive(Clone, Copy, Debug, Eq, PartialEq)]
5324pub struct CppDisplacedPreprocessorBoundary {
5325    pub end_byte: usize,
5326    pub end_line: usize,
5327}
5328
5329pub fn cpp_displaced_preprocessor_boundary(
5330    conditional: Node<'_>,
5331) -> Option<CppDisplacedPreprocessorBoundary> {
5332    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
5333        return Some(CppDisplacedPreprocessorBoundary {
5334            end_byte: terminator.end_byte(),
5335            end_line: terminator.end_position().row + 1,
5336        });
5337    }
5338    if let Some(declaration) = displaced_split_declaration(conditional) {
5339        return Some(CppDisplacedPreprocessorBoundary {
5340            end_byte: declaration.end_byte(),
5341            end_line: declaration.end_position().row + 1,
5342        });
5343    }
5344    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
5345        return Some(CppDisplacedPreprocessorBoundary {
5346            end_byte: terminator.end_byte(),
5347            end_line: terminator.end_position().row + 1,
5348        });
5349    }
5350    None
5351}
5352
5353fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5354    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
5355        return None;
5356    }
5357    let mut cursor = conditional.walk();
5358    let declarations = conditional
5359        .named_children(&mut cursor)
5360        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
5361        .collect::<Vec<_>>();
5362    let declaration = *declarations.first()?;
5363    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
5364        return None;
5365    }
5366    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
5367    let mut terminator = None;
5368    let mut stack = (0..declaration.child_count())
5369        .filter_map(|index| declaration.child(index))
5370        .filter(|child| child.start_byte() < declarator_start)
5371        .map(|child| (child, false))
5372        .collect::<Vec<_>>();
5373    while let Some((node, inside_error)) = stack.pop() {
5374        let inside_error = inside_error || node.kind() == "ERROR";
5375        if inside_error && node.kind() == "#endif" && !node.is_missing() {
5376            terminator = Some(node);
5377            continue;
5378        }
5379        for index in 0..node.child_count() {
5380            if let Some(child) = node.child(index)
5381                && child.start_byte() < declarator_start
5382            {
5383                stack.push((child, inside_error));
5384            }
5385        }
5386    }
5387    terminator
5388}
5389
5390fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
5391    if !conditional.has_error()
5392        || conditional.child_by_field_name("alternative").is_some()
5393        || conditional
5394            .prev_named_sibling()
5395            .filter(|sibling| {
5396                sibling.kind() == "ERROR"
5397                    && sibling.child_count() == 1
5398                    && sibling
5399                        .child(0)
5400                        .is_some_and(|child| child.kind() == "typedef")
5401            })
5402            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
5403            .is_none()
5404    {
5405        return None;
5406    }
5407    let mut cursor = conditional.walk();
5408    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
5409    let declaration_index = children
5410        .iter()
5411        .position(|child| child.kind() == "declaration" && child.has_error())?;
5412    let declaration = children[declaration_index];
5413    if !children
5414        .iter()
5415        .skip(declaration_index + 1)
5416        .any(|child| child.end_byte() > declaration.end_byte())
5417    {
5418        return None;
5419    }
5420    let declarator = declaration.child_by_field_name("declarator")?;
5421    let mut error_end = None;
5422    let mut names = Vec::new();
5423    let mut stack = vec![declarator];
5424    while let Some(node) = stack.pop() {
5425        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
5426            error_end =
5427                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
5428            continue;
5429        }
5430        if matches!(node.kind(), "identifier" | "type_identifier") {
5431            names.push(node.start_byte());
5432        }
5433        for index in (0..node.named_child_count()).rev() {
5434            if let Some(child) = node.named_child(index) {
5435                stack.push(child);
5436            }
5437        }
5438    }
5439    let error_end = error_end?;
5440    names
5441        .into_iter()
5442        .any(|start| start >= error_end)
5443        .then_some(declaration)
5444}
5445
5446fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
5447    let Some(error) = parent.child(error_index) else {
5448        return false;
5449    };
5450    if error.kind() != "ERROR"
5451        || error.child_count() != 1
5452        || error.child(0).is_none_or(|child| child.kind() != "}")
5453    {
5454        return false;
5455    }
5456    let Some(semicolon) = parent.child(error_index + 1) else {
5457        return false;
5458    };
5459    semicolon.kind() == "expression_statement"
5460        && semicolon.child_count() == 1
5461        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
5462}
5463
5464/// Locate the real end of a class-like declaration when a macro invocation
5465/// without a source semicolon absorbs the class's `};` into its parsed field.
5466/// The grammar then keeps following namespace declarations as later children
5467/// of the same field list. The direct ERROR-plus-semicolon pair proves the
5468/// boundary structurally; no source-text delimiter scan is needed.
5469fn displaced_macro_class_tail(
5470    declaration_node: Node<'_>,
5471    body: Node<'_>,
5472    source: &str,
5473) -> Option<DisplacedMacroClassTail> {
5474    if !matches!(
5475        declaration_node.kind(),
5476        "class_specifier" | "struct_specifier" | "union_specifier"
5477    ) || body.kind() != "field_declaration_list"
5478    {
5479        return None;
5480    }
5481
5482    let child_count = body.named_child_count();
5483    for index in 0..child_count {
5484        let child = body.named_child(index)?;
5485        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
5486            continue;
5487        };
5488        let split_index = index + 1;
5489        if split_index >= child_count {
5490            return None;
5491        }
5492        let mut cursor = body.walk();
5493        if !body
5494            .named_children(&mut cursor)
5495            .skip(split_index)
5496            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
5497        {
5498            return None;
5499        }
5500        return Some(DisplacedMacroClassTail {
5501            split_index,
5502            class_range: Range {
5503                start_byte: declaration_node.start_byte(),
5504                end_byte: terminator.end_byte(),
5505                start_line: declaration_node.start_position().row + 1,
5506                end_line: terminator.end_position().row + 1,
5507            },
5508        });
5509    }
5510    None
5511}
5512
5513fn displaced_macro_field_terminator<'tree>(
5514    field: Node<'tree>,
5515    source: &str,
5516) -> Option<Node<'tree>> {
5517    if field.kind() != "field_declaration" {
5518        return None;
5519    }
5520    let macro_type = field.child_by_field_name("type")?;
5521    if macro_type.kind() != "type_identifier"
5522        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
5523        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
5524    {
5525        return None;
5526    }
5527    for index in 0..field.child_count() {
5528        let error = field.child(index)?;
5529        if error.kind() != "ERROR"
5530            || error.child_count() != 1
5531            || error.child(0).is_none_or(|child| child.kind() != "}")
5532        {
5533            continue;
5534        }
5535        let semicolon = field.child(index + 1)?;
5536        if semicolon.kind() == ";" {
5537            return Some(semicolon);
5538        }
5539    }
5540    None
5541}
5542
5543fn recover_fragmented_partial_specialization<'tree>(
5544    template_node: Node<'tree>,
5545    declaration_child: Node<'tree>,
5546    source: &str,
5547) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
5548    if declaration_child.kind() != "function_definition" {
5549        return None;
5550    }
5551    let class_node = declaration_child.child_by_field_name("type")?;
5552    if !matches!(
5553        class_node.kind(),
5554        "class_specifier" | "struct_specifier" | "union_specifier"
5555    ) || !class_node
5556        .child_by_field_name("name")
5557        .and_then(|name| direct_identifier_name(name, source))
5558        .is_some_and(|name| cpp_export_macro_token(&name))
5559    {
5560        return None;
5561    }
5562    let declarator = declaration_child.child_by_field_name("declarator")?;
5563    if declarator.kind() != "template_function" {
5564        return None;
5565    }
5566    let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
5567    if metadata.specialization_arguments.is_empty() {
5568        return None;
5569    }
5570    let body = declaration_child.child_by_field_name("body")?;
5571    if body.kind() != "compound_statement" {
5572        return None;
5573    }
5574    let complete_prefix = body.named_child(0).filter(|first| {
5575        first.kind() == "labeled_statement"
5576            && first.has_error()
5577            && first
5578                .named_child(first.named_child_count().saturating_sub(1))
5579                .is_some_and(recovered_declaration_has_class_terminator)
5580    });
5581    let complete_body = complete_prefix.is_some();
5582    let mut prefix_members = Vec::new();
5583    if let Some(prefix) = complete_prefix {
5584        prefix_members.push(prefix);
5585    } else {
5586        let mut body_cursor = body.walk();
5587        for child in body.named_children(&mut body_cursor) {
5588            if !is_structurally_valid_fragmented_class_prefix_member(child) {
5589                break;
5590            }
5591            prefix_members.push(child);
5592        }
5593    }
5594    let containing_declarations = template_node.parent()?;
5595    if !matches!(
5596        containing_declarations.kind(),
5597        "declaration_list" | "compound_statement"
5598    ) {
5599        return None;
5600    }
5601    let mut member_siblings = Vec::new();
5602    let mut following_declarations = Vec::new();
5603    let terminator;
5604    if complete_body {
5605        terminator = complete_prefix?;
5606        let mut cursor = body.walk();
5607        let mut after_prefix = false;
5608        for child in body.named_children(&mut cursor) {
5609            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
5610                after_prefix = true;
5611            } else if after_prefix {
5612                following_declarations.push(child);
5613            }
5614        }
5615    } else {
5616        let mut found_template = false;
5617        let mut cursor = containing_declarations.walk();
5618        let mut class_terminator = None;
5619        for child in containing_declarations.children(&mut cursor) {
5620            if same_node(child, template_node) {
5621                found_template = true;
5622                continue;
5623            }
5624            if found_template && child.kind() == "}" {
5625                class_terminator = Some(child);
5626                break;
5627            }
5628            if found_template && child.is_named() {
5629                member_siblings.push(child);
5630            }
5631        }
5632        terminator = class_terminator?;
5633    }
5634    let name = format!(
5635        "{}<{}>",
5636        metadata.primary_name,
5637        metadata
5638            .specialization_arguments
5639            .iter()
5640            .map(|argument| argument.text.as_str())
5641            .collect::<Vec<_>>()
5642            .join(", ")
5643    );
5644    Some(RecoveredFragmentedPartialSpecialization {
5645        declaration_node: declaration_child,
5646        name,
5647        range: Range {
5648            start_byte: declaration_child.start_byte(),
5649            end_byte: terminator.end_byte(),
5650            start_line: declaration_child.start_position().row + 1,
5651            end_line: terminator.end_position().row + 1,
5652        },
5653        prefix_members,
5654        member_siblings,
5655        following_declarations,
5656    })
5657}
5658
5659fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
5660    if declaration.kind() != "declaration" {
5661        return false;
5662    }
5663    // With an export macro between `class` and its name, tree-sitter folds a
5664    // complete class body into a function-shaped declaration. The class's own
5665    // `};` remains structurally identifiable as a direct ERROR child holding
5666    // `}`, immediately followed by the declaration's direct `;` child.
5667    (0..declaration.child_count().saturating_sub(1)).any(|index| {
5668        let Some(error) = declaration.child(index) else {
5669            return false;
5670        };
5671        error.kind() == "ERROR"
5672            && error.child_count() == 1
5673            && error.child(0).is_some_and(|child| child.kind() == "}")
5674            && declaration
5675                .child(index + 1)
5676                .is_some_and(|child| child.kind() == ";")
5677    })
5678}
5679
5680fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
5681    if node.has_error() {
5682        return false;
5683    }
5684    match node.kind() {
5685        "declaration"
5686        | "field_declaration"
5687        | "alias_declaration"
5688        | "type_definition"
5689        | "static_assert_declaration" => true,
5690        "labeled_statement" => node
5691            .named_child(node.named_child_count().saturating_sub(1))
5692            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
5693        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
5694            matches!(
5695                child.kind(),
5696                "declaration"
5697                    | "field_declaration"
5698                    | "alias_declaration"
5699                    | "type_definition"
5700                    | "function_definition"
5701            )
5702        }),
5703        _ => false,
5704    }
5705}
5706
5707fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
5708    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
5709        .then(|| node.child_by_field_name("declarator"))
5710        .flatten()
5711        .and_then(|declarator| extract_variable_name(declarator, source))
5712}
5713
5714fn cpp_template_metadata(
5715    template_node: Node<'_>,
5716    declaration_child: Node<'_>,
5717    source: &str,
5718) -> Option<CppTemplateMetadata> {
5719    let parameters_node = template_node.child_by_field_name("parameters")?;
5720    let name_node = cpp_templated_class_name_node(declaration_child)?;
5721    let primary_node = match name_node.kind() {
5722        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
5723        _ => name_node,
5724    };
5725    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
5726    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
5727        return None;
5728    }
5729
5730    let mut parameter_nodes = Vec::new();
5731    let mut parameter_names = Vec::new();
5732    let mut cursor = parameters_node.walk();
5733    for parameter in parameters_node.named_children(&mut cursor) {
5734        let Some(name) = cpp_template_parameter_name(parameter, source) else {
5735            continue;
5736        };
5737        parameter_names.push(name);
5738        parameter_nodes.push(parameter);
5739    }
5740    let parameters = parameter_nodes
5741        .into_iter()
5742        .zip(parameter_names.iter().cloned())
5743        .map(|(parameter, name)| CppTemplateParameterMetadata {
5744            name,
5745            kind: cpp_template_parameter_kind(parameter),
5746            variadic: matches!(
5747                parameter.kind(),
5748                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
5749            ),
5750            default: cpp_template_parameter_default_expression(parameter, source, &parameter_names),
5751        })
5752        .collect();
5753    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
5754        Vec::new()
5755    } else {
5756        cpp_template_argument_expressions(name_node, source, &parameter_names).unwrap_or_default()
5757    };
5758    let alias_target = (declaration_child.kind() == "alias_declaration")
5759        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names))
5760        .flatten();
5761    Some(CppTemplateMetadata {
5762        primary_name,
5763        primary_fq_name: String::new(),
5764        parameters,
5765        specialization_arguments,
5766        alias_target,
5767    })
5768}
5769
5770fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
5771    match node.kind() {
5772        "class_specifier" | "struct_specifier" | "union_specifier" => {
5773            node.child_by_field_name("name")
5774        }
5775        "function_definition" => {
5776            let declarator = node.child_by_field_name("declarator")?;
5777            if matches!(declarator.kind(), "identifier" | "template_function") {
5778                Some(declarator)
5779            } else {
5780                None
5781            }
5782        }
5783        "alias_declaration" => node.child_by_field_name("name"),
5784        _ => None,
5785    }
5786}
5787
5788fn cpp_template_alias_target(
5789    alias: Node<'_>,
5790    source: &str,
5791    parameter_names: &[String],
5792) -> Option<CppTemplateAliasTargetMetadata> {
5793    let mut type_node = alias.child_by_field_name("type")?;
5794    while type_node.kind() == "type_descriptor" {
5795        type_node = type_node.child_by_field_name("type")?;
5796    }
5797    let global = type_node.child_by_field_name("scope").is_none()
5798        && type_node.child(0).is_some_and(|child| child.kind() == "::");
5799    let mut components = Vec::new();
5800    cpp_template_target_components(type_node, source, &mut components)?;
5801    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
5802    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
5803        components,
5804        global,
5805        arguments,
5806    })
5807}
5808
5809fn cpp_template_target_components(
5810    node: Node<'_>,
5811    source: &str,
5812    out: &mut Vec<String>,
5813) -> Option<()> {
5814    match node.kind() {
5815        "identifier" | "namespace_identifier" | "type_identifier" => {
5816            out.push(node_text(node, source).to_string());
5817            Some(())
5818        }
5819        "template_type" => {
5820            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5821        }
5822        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
5823            if let Some(scope) = node.child_by_field_name("scope") {
5824                cpp_template_target_components(scope, source, out)?;
5825            }
5826            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
5827        }
5828        _ => None,
5829    }
5830}
5831
5832fn cpp_template_argument_expressions(
5833    mut node: Node<'_>,
5834    source: &str,
5835    parameter_names: &[String],
5836) -> Option<Vec<CppTemplateExpression>> {
5837    loop {
5838        match node.kind() {
5839            "template_type" | "template_function" => {
5840                let arguments = node.child_by_field_name("arguments")?;
5841                let mut cursor = arguments.walk();
5842                return Some(
5843                    arguments
5844                        .named_children(&mut cursor)
5845                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
5846                        .map(|argument| cpp_template_expression(argument, source, parameter_names))
5847                        .collect(),
5848                );
5849            }
5850            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
5851                node = node
5852                    .child_by_field_name("name")
5853                    .or_else(|| node.child_by_field_name("type"))?;
5854            }
5855            _ => return None,
5856        }
5857    }
5858}
5859
5860fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
5861    let candidate = node
5862        .child_by_field_name("name")
5863        .or_else(|| node.child_by_field_name("declarator"))
5864        .or_else(|| {
5865            let mut cursor = node.walk();
5866            node.named_children(&mut cursor).find(|child| {
5867                matches!(
5868                    child.kind(),
5869                    "identifier" | "type_identifier" | "field_identifier"
5870                )
5871            })
5872        })?;
5873    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
5874    (!name.is_empty()).then_some(name)
5875}
5876
5877fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
5878    match node.kind() {
5879        "type_parameter_declaration"
5880        | "optional_type_parameter_declaration"
5881        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
5882        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
5883        _ => CppTemplateParameterKind::Value,
5884    }
5885}
5886
5887fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
5888    node.child_by_field_name("default_type")
5889        .or_else(|| node.child_by_field_name("default_value"))
5890}
5891
5892fn cpp_template_parameter_default_expression(
5893    parameter: Node<'_>,
5894    source: &str,
5895    parameter_names: &[String],
5896) -> Option<CppTemplateExpression> {
5897    let default = cpp_template_parameter_default(parameter)?;
5898    let base = cpp_template_expression(default, source, parameter_names);
5899    let Some(pointer_error) = parameter.next_named_sibling() else {
5900        return Some(base);
5901    };
5902    let Some(pointer_declarator) =
5903        recovered_abstract_pointer_declarator_term(pointer_error, source)
5904    else {
5905        return Some(base);
5906    };
5907    Some(CppTemplateExpression {
5908        text: format!(
5909            "{}{}",
5910            base.text,
5911            normalize_cpp_whitespace(node_text(pointer_error, source))
5912        ),
5913        term: CppTemplateTerm::Node {
5914            kind: "type_descriptor".to_string(),
5915            children: vec![base.term, pointer_declarator],
5916        },
5917    })
5918}
5919
5920fn recovered_abstract_pointer_declarator_term(
5921    node: Node<'_>,
5922    source: &str,
5923) -> Option<CppTemplateTerm> {
5924    if node.kind() != "ERROR" || node.child_count() == 0 {
5925        return None;
5926    }
5927    let mut children = Vec::new();
5928    for index in 0..node.child_count() {
5929        let child = node.child(index)?;
5930        if child.kind() != "*" {
5931            return None;
5932        }
5933        children.push(CppTemplateTerm::Atom {
5934            kind: "*".to_string(),
5935            text: normalize_cpp_whitespace(node_text(child, source)),
5936        });
5937    }
5938    Some(CppTemplateTerm::Node {
5939        kind: "abstract_pointer_declarator".to_string(),
5940        children,
5941    })
5942}
5943
5944fn cpp_template_expression(
5945    node: Node<'_>,
5946    source: &str,
5947    parameter_names: &[String],
5948) -> CppTemplateExpression {
5949    let text = normalize_cpp_whitespace(node_text(node, source));
5950    CppTemplateExpression {
5951        text,
5952        term: cpp_template_term(node, source, parameter_names),
5953    }
5954}
5955
5956pub fn cpp_template_term(
5957    node: Node<'_>,
5958    source: &str,
5959    parameter_names: &[String],
5960) -> CppTemplateTerm {
5961    enum Work<'tree> {
5962        Visit(Node<'tree>),
5963        Build { kind: String, child_count: usize },
5964    }
5965
5966    let mut work = vec![Work::Visit(node)];
5967    let mut terms = Vec::new();
5968    while let Some(next) = work.pop() {
5969        match next {
5970            Work::Visit(current) => {
5971                let text = normalize_cpp_whitespace(node_text(current, source));
5972                if parameter_names.contains(&text) {
5973                    terms.push(CppTemplateTerm::Parameter(text));
5974                    continue;
5975                }
5976                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
5977                    let mut cursor = current.walk();
5978                    let named = current
5979                        .named_children(&mut cursor)
5980                        .filter(|child| !child.is_extra() && child.kind() != "comment")
5981                        .collect::<Vec<_>>();
5982                    if let [child] = named.as_slice() {
5983                        work.push(Work::Visit(*child));
5984                        continue;
5985                    }
5986                }
5987                if current.child_count() == 0 {
5988                    terms.push(CppTemplateTerm::Atom {
5989                        kind: if matches!(
5990                            current.kind(),
5991                            "identifier"
5992                                | "type_identifier"
5993                                | "field_identifier"
5994                                | "namespace_identifier"
5995                        ) {
5996                            "identifier".to_string()
5997                        } else {
5998                            current.kind().to_string()
5999                        },
6000                        text,
6001                    });
6002                    continue;
6003                }
6004                let children = (0..current.child_count())
6005                    .filter_map(|index| current.child(index))
6006                    .filter(|child| !child.is_extra() && child.kind() != "comment")
6007                    .collect::<Vec<_>>();
6008                work.push(Work::Build {
6009                    kind: current.kind().to_string(),
6010                    child_count: children.len(),
6011                });
6012                work.extend(children.into_iter().rev().map(Work::Visit));
6013            }
6014            Work::Build { kind, child_count } => {
6015                let children = terms.split_off(terms.len() - child_count);
6016                terms.push(CppTemplateTerm::Node { kind, children });
6017            }
6018        }
6019    }
6020    terms.pop().expect("template term traversal emits one root")
6021}
6022
6023fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6024    loop {
6025        match node.kind() {
6026            "declaration"
6027            | "function_declaration"
6028            | "field_declaration"
6029            | "function_definition" => return Some(node),
6030            _ => node = node.parent()?,
6031        }
6032    }
6033}
6034
6035fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6036    let mut params = Vec::new();
6037    let mut cursor = parameters_node.walk();
6038    for child in parameters_node.children(&mut cursor) {
6039        match child.kind() {
6040            "parameter_declaration" | "optional_parameter_declaration" => {
6041                params.push(cpp_parameter_type(child, source));
6042            }
6043            "variadic_parameter_declaration" => {
6044                params.push(cpp_parameter_type(child, source));
6045            }
6046            "variadic_parameter" | "..." => params.push("...".to_string()),
6047            _ => {}
6048        }
6049    }
6050
6051    if params.is_empty() {
6052        "()".to_string()
6053    } else {
6054        format!("({})", params.join(", "))
6055    }
6056}
6057
6058fn cpp_signature_metadata(
6059    signature: String,
6060    function_declarator: Node<'_>,
6061    source: &str,
6062) -> SignatureMetadata {
6063    let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6064    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6065    let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6066    let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6067    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6068        return enrich(
6069            SignatureMetadata::new(signature, Vec::new())
6070                .with_return_type_text(return_type_text)
6071                .with_return_type_identity(return_type_identity),
6072        );
6073    };
6074    let callable_arity = cpp_callable_arity(parameters_node, source);
6075    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6076    let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6077    let Some(relative_start) = signature
6078        .get(search_from..)
6079        .and_then(|suffix| suffix.find(&parameter_text))
6080    else {
6081        return enrich(
6082            SignatureMetadata::new(signature, Vec::new())
6083                .with_callable_arity(callable_arity)
6084                .with_return_type_text(return_type_text)
6085                .with_return_type_identity(return_type_identity),
6086        );
6087    };
6088    let parameters_start = search_from + relative_start;
6089    let parameters_end = parameters_start + parameter_text.len();
6090    let mut search_start = parameters_start;
6091    let parameters = cpp_parameter_label_nodes(parameters_node)
6092        .into_iter()
6093        .filter_map(|label_node| {
6094            let label = normalize_cpp_whitespace(node_text(label_node, source));
6095            if label.is_empty() || search_start > parameters_end {
6096                return None;
6097            }
6098            let haystack = signature.get(search_start..parameters_end)?;
6099            let relative_start = haystack.find(&label)?;
6100            let start_byte = search_start + relative_start;
6101            let end_byte = start_byte + label.len();
6102            search_start = end_byte;
6103            Some(ParameterMetadata::new(label, start_byte, end_byte))
6104        })
6105        .collect();
6106    enrich(
6107        SignatureMetadata::new(signature, parameters)
6108            .with_callable_arity(callable_arity)
6109            .with_return_type_text(return_type_text)
6110            .with_return_type_identity(return_type_identity),
6111    )
6112}
6113
6114fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6115    let Some(name_node) = function_declarator
6116        .child_by_field_name("declarator")
6117        .or_else(|| function_declarator.child_by_field_name("name"))
6118        .or_else(|| last_named_child(function_declarator))
6119    else {
6120        return false;
6121    };
6122    let Some(callable_name) = direct_identifier_name(name_node, source) else {
6123        return false;
6124    };
6125
6126    let mut current = function_declarator.parent();
6127    while let Some(ancestor) = current {
6128        let owner_name = match ancestor.kind() {
6129            "class_specifier" | "struct_specifier" | "union_specifier" => {
6130                class_like_name(ancestor, source)
6131            }
6132            "ERROR" => malformed_class_error_owner_name(ancestor, source),
6133            _ => None,
6134        };
6135        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6136            return true;
6137        }
6138        current = ancestor.parent();
6139    }
6140    false
6141}
6142
6143/// Recover the owner name from the direct grammar shape retained when a later
6144/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
6145/// `ERROR` node:
6146///
6147/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
6148///
6149/// Direct-child checks keep this distinct from an unrelated nested class inside
6150/// a broader error region. The closing brace may be displaced past the error
6151/// node, so the opening body token is the available structural boundary.
6152fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6153    if node.kind() != "ERROR" {
6154        return None;
6155    }
6156    let keyword = node.child(0)?;
6157    if !matches!(keyword.kind(), "class" | "struct" | "union") {
6158        return None;
6159    }
6160    let name_node = node.child(1)?;
6161    let name = direct_identifier_name(name_node, source)?;
6162    let has_body = (2..node.child_count())
6163        .filter_map(|index| node.child(index))
6164        .any(|child| child.kind() == "{");
6165    has_body.then_some(name)
6166}
6167
6168fn cpp_callable_return_type_identity(
6169    function_declarator: Node<'_>,
6170    source: &str,
6171) -> Option<StructuredTypeIdentity> {
6172    if cpp_callable_is_structural_constructor(function_declarator, source) {
6173        return None;
6174    }
6175    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
6176    let mut cursor = function_declarator.walk();
6177    if let Some(trailing) = function_declarator
6178        .named_children(&mut cursor)
6179        .find(|child| child.kind() == "trailing_return_type")
6180        && let Some(type_descriptor) = trailing.named_child(0)
6181    {
6182        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
6183    }
6184
6185    let mut current = function_declarator;
6186    let mut wrappers = Vec::new();
6187    while let Some(parent) = current.parent() {
6188        if matches!(
6189            parent.kind(),
6190            "function_definition" | "declaration" | "field_declaration"
6191        ) {
6192            let type_node = parent.child_by_field_name("type")?;
6193            if cpp_export_macro_token(node_text(type_node, source))
6194                && (0..parent.named_child_count()).any(|index| {
6195                    parent
6196                        .named_child(index)
6197                        .is_some_and(|child| child.kind() == "ERROR")
6198                })
6199            {
6200                return None;
6201            }
6202            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
6203            for wrapper in wrappers.into_iter().rev() {
6204                identity = cpp_wrap_structured_type(identity, wrapper)?;
6205            }
6206            return Some(identity);
6207        }
6208        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6209            || (matches!(
6210                parent.kind(),
6211                "pointer_declarator"
6212                    | "reference_declarator"
6213                    | "array_declarator"
6214                    | "parenthesized_declarator"
6215            ) && parent.named_child_count() == 1
6216                && parent.named_child(0) == Some(current));
6217        if !wraps_current_declarator {
6218            return None;
6219        }
6220        match parent.kind() {
6221            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
6222            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6223            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
6224            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
6225            _ => return None,
6226        }
6227        current = parent;
6228    }
6229    None
6230}
6231
6232fn cpp_structured_type_identity(
6233    node: Node<'_>,
6234    source: &str,
6235    lexical_scope: &[String],
6236) -> Option<StructuredTypeIdentity> {
6237    enum Work<'tree> {
6238        Visit(Node<'tree>),
6239        Wrap(CppStructuredTypeWrapper),
6240        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
6241        BuildGeneric { argument_count: usize },
6242    }
6243
6244    let mut work = vec![Work::Visit(node)];
6245    let mut values = Vec::new();
6246    let mut builder = StructuredTypeIdentityBuilder::default();
6247    while let Some(next) = work.pop() {
6248        match next {
6249            Work::Visit(current) => match current.kind() {
6250                "type_descriptor" => {
6251                    let type_node = current
6252                        .child_by_field_name("type")
6253                        .or_else(|| current.named_child(0))?;
6254                    let mut wrappers = Vec::new();
6255                    let mut cursor = current.walk();
6256                    for child in current.named_children(&mut cursor) {
6257                        if child.id() != type_node.id() {
6258                            wrappers.extend(cpp_structured_declarator_wrappers(child));
6259                        }
6260                    }
6261                    work.push(Work::ApplyWrappers(wrappers));
6262                    work.push(Work::Visit(type_node));
6263                }
6264                "pointer_declarator" | "abstract_pointer_declarator" => {
6265                    let child = current
6266                        .child_by_field_name("declarator")
6267                        .or_else(|| current.named_child(0))?;
6268                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
6269                    work.push(Work::Visit(child));
6270                }
6271                "reference_declarator" => {
6272                    let child = current
6273                        .child_by_field_name("declarator")
6274                        .or_else(|| current.named_child(0))?;
6275                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
6276                    work.push(Work::Visit(child));
6277                }
6278                "array_declarator" | "abstract_array_declarator" => {
6279                    let child = current
6280                        .child_by_field_name("declarator")
6281                        .or_else(|| current.named_child(0))?;
6282                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
6283                    work.push(Work::Visit(child));
6284                }
6285                "template_type" => {
6286                    let name_node = current.child_by_field_name("name")?;
6287                    let arguments = current
6288                        .child_by_field_name("arguments")
6289                        .map(|arguments_node| {
6290                            let mut cursor = arguments_node.walk();
6291                            arguments_node
6292                                .named_children(&mut cursor)
6293                                .filter(|child| !child.is_extra() && child.kind() != "comment")
6294                                .collect::<Vec<_>>()
6295                        })
6296                        .unwrap_or_default();
6297                    work.push(Work::BuildGeneric {
6298                        argument_count: arguments.len(),
6299                    });
6300                    work.extend(arguments.into_iter().rev().map(Work::Visit));
6301                    work.push(Work::Visit(name_node));
6302                }
6303                "qualified_identifier"
6304                | "scoped_identifier"
6305                | "scoped_type_identifier"
6306                | "type_identifier"
6307                | "identifier"
6308                | "namespace_identifier"
6309                | "primitive_type" => {
6310                    values.push(builder.named(cpp_structured_named_type(
6311                        current,
6312                        source,
6313                        lexical_scope,
6314                    )?)?);
6315                }
6316                _ => {
6317                    let child = current.child_by_field_name("type").or_else(|| {
6318                        (current.named_child_count() == 1)
6319                            .then(|| current.named_child(0))
6320                            .flatten()
6321                    })?;
6322                    work.push(Work::Visit(child));
6323                }
6324            },
6325            Work::Wrap(wrapper) => {
6326                let root = values.pop()?;
6327                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
6328            }
6329            Work::ApplyWrappers(wrappers) => {
6330                let mut root = values.pop()?;
6331                for wrapper in wrappers.into_iter().rev() {
6332                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
6333                }
6334                values.push(root);
6335            }
6336            Work::BuildGeneric { argument_count } => {
6337                let value_count = argument_count.checked_add(1)?;
6338                let start = values.len().checked_sub(value_count)?;
6339                let mut built = values.split_off(start);
6340                let base = built.remove(0);
6341                values.push(builder.generic(base, built)?);
6342            }
6343        }
6344    }
6345    (values.len() == 1)
6346        .then(|| values.pop())
6347        .flatten()
6348        .and_then(|root| builder.finish(root))
6349}
6350
6351fn cpp_structured_named_type(
6352    node: Node<'_>,
6353    source: &str,
6354    lexical_scope: &[String],
6355) -> Option<StructuredTypeName> {
6356    let path = cpp_structured_type_path(node, source)?;
6357    let absolute = node.child_by_field_name("scope").is_none()
6358        && node.child(0).is_some_and(|child| child.kind() == "::");
6359    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
6360}
6361
6362#[derive(Clone, Copy)]
6363enum CppStructuredTypeWrapper {
6364    Pointer,
6365    Reference,
6366    Array,
6367}
6368
6369fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
6370    let mut wrappers = Vec::new();
6371    let mut current = node;
6372    loop {
6373        match current.kind() {
6374            "pointer_declarator" | "abstract_pointer_declarator" => {
6375                wrappers.push(CppStructuredTypeWrapper::Pointer)
6376            }
6377            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
6378            "array_declarator" | "abstract_array_declarator" => {
6379                wrappers.push(CppStructuredTypeWrapper::Array)
6380            }
6381            _ => break,
6382        }
6383        let Some(child) = current
6384            .child_by_field_name("declarator")
6385            .or_else(|| current.named_child(0))
6386        else {
6387            break;
6388        };
6389        current = child;
6390    }
6391    wrappers
6392}
6393
6394fn cpp_wrap_structured_type(
6395    identity: StructuredTypeIdentity,
6396    wrapper: CppStructuredTypeWrapper,
6397) -> Option<StructuredTypeIdentity> {
6398    match wrapper {
6399        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
6400        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
6401        CppStructuredTypeWrapper::Array => identity.wrap_array(),
6402    }
6403}
6404
6405fn cpp_wrap_structured_type_node(
6406    builder: &mut StructuredTypeIdentityBuilder,
6407    inner: StructuredTypeNodeId,
6408    wrapper: CppStructuredTypeWrapper,
6409) -> Option<StructuredTypeNodeId> {
6410    match wrapper {
6411        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
6412        CppStructuredTypeWrapper::Reference => builder.reference(inner),
6413        CppStructuredTypeWrapper::Array => builder.array(inner),
6414    }
6415}
6416
6417fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
6418    let mut path = Vec::new();
6419    let mut stack = vec![node];
6420    while let Some(current) = stack.pop() {
6421        match current.kind() {
6422            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
6423                let component = node_text(current, source).to_string();
6424                if component.is_empty() {
6425                    return None;
6426                }
6427                path.push(component);
6428            }
6429            "template_type" | "dependent_type" => {
6430                stack.push(current.child_by_field_name("name")?);
6431            }
6432            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6433                stack.push(current.child_by_field_name("name")?);
6434                if let Some(scope) = current.child_by_field_name("scope") {
6435                    stack.push(scope);
6436                }
6437            }
6438            _ => return None,
6439        }
6440    }
6441    (!path.is_empty()).then_some(path)
6442}
6443
6444fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
6445    let mut groups = Vec::new();
6446    let mut current = node.parent();
6447    while let Some(parent) = current {
6448        if matches!(
6449            parent.kind(),
6450            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
6451        ) && let Some(name_node) = parent.child_by_field_name("name")
6452            && let Some(components) = cpp_structured_type_path(name_node, source)
6453            && !components.is_empty()
6454        {
6455            groups.push(components);
6456        }
6457        current = parent.parent();
6458    }
6459    groups.reverse();
6460    groups.into_iter().flatten().collect()
6461}
6462
6463fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
6464    let mut declaration = None;
6465    let mut current = Some(function_declarator);
6466    while let Some(node) = current {
6467        match node.kind() {
6468            "template_declaration"
6469            | "preproc_if"
6470            | "preproc_ifdef"
6471            | "preproc_else"
6472            | "preproc_elif"
6473            | "preproc_call"
6474            | "ERROR" => return DispatchExtensibility::Open,
6475            "declaration" | "field_declaration" | "function_definition" => {
6476                declaration.get_or_insert(node);
6477            }
6478            "translation_unit" => break,
6479            _ => {}
6480        }
6481        current = node.parent();
6482    }
6483    let Some(declaration) = declaration else {
6484        return DispatchExtensibility::Open;
6485    };
6486
6487    let mut saw_virtual_boundary = false;
6488    let mut stack = vec![declaration];
6489    while let Some(node) = stack.pop() {
6490        match node.kind() {
6491            "compound_statement" | "field_declaration_list" => continue,
6492            "final" | "final_specifier" => return DispatchExtensibility::Closed,
6493            "virtual"
6494            | "override"
6495            | "virtual_specifier"
6496            | "pure_virtual_clause"
6497            | "template_parameter_list"
6498            | "template_method"
6499            | "template_function"
6500            | "ERROR" => saw_virtual_boundary = true,
6501            _ => {}
6502        }
6503        let mut cursor = node.walk();
6504        stack.extend(node.children(&mut cursor));
6505    }
6506
6507    if saw_virtual_boundary {
6508        DispatchExtensibility::Open
6509    } else {
6510        DispatchExtensibility::Closed
6511    }
6512}
6513
6514fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
6515    let mut enclosed_by_class = false;
6516    let mut current = declaration.parent();
6517    while let Some(node) = current {
6518        if node.kind() == "namespace_definition"
6519            && node
6520                .child_by_field_name("name")
6521                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6522        {
6523            return CallableLinkage::Internal;
6524        }
6525        if matches!(
6526            node.kind(),
6527            "class_specifier" | "struct_specifier" | "union_specifier"
6528        ) {
6529            if node
6530                .child_by_field_name("name")
6531                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
6532            {
6533                return CallableLinkage::Internal;
6534            }
6535            enclosed_by_class = true;
6536        }
6537        if matches!(node.kind(), "function_definition" | "lambda_expression") {
6538            return CallableLinkage::Internal;
6539        }
6540        current = node.parent();
6541    }
6542
6543    if enclosed_by_class {
6544        return CallableLinkage::External;
6545    }
6546
6547    let mut cursor = declaration.walk();
6548    if declaration.named_children(&mut cursor).any(|child| {
6549        child.kind() == "storage_class_specifier"
6550            && normalize_cpp_whitespace(node_text(child, source)) == "static"
6551    }) {
6552        CallableLinkage::Internal
6553    } else {
6554        CallableLinkage::External
6555    }
6556}
6557
6558fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
6559    if cpp_callable_is_structural_constructor(function_declarator, source) {
6560        return None;
6561    }
6562    let mut cursor = function_declarator.walk();
6563    if let Some(trailing) = function_declarator
6564        .named_children(&mut cursor)
6565        .find(|child| child.kind() == "trailing_return_type")
6566        && let Some(type_descriptor) = trailing.named_child(0)
6567    {
6568        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
6569        if !text.is_empty() {
6570            return Some(text);
6571        }
6572    }
6573
6574    let mut current = function_declarator;
6575    let mut indirection = String::new();
6576    while let Some(parent) = current.parent() {
6577        if matches!(
6578            parent.kind(),
6579            "function_definition" | "declaration" | "field_declaration"
6580        ) {
6581            let type_node = parent.child_by_field_name("type")?;
6582            if cpp_export_macro_token(node_text(type_node, source))
6583                && (0..parent.named_child_count()).any(|index| {
6584                    parent
6585                        .named_child(index)
6586                        .is_some_and(|child| child.kind() == "ERROR")
6587                })
6588            {
6589                // Export/decorator macros commonly occupy the grammar's `type`
6590                // field and leave the semantic return type in an ERROR sibling.
6591                // Do not persist the macro token as a return type. The malformed
6592                // declaration does not carry enough structured evidence here.
6593                return None;
6594            }
6595            let base = normalize_cpp_whitespace(node_text(type_node, source));
6596            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
6597        }
6598        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
6599            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
6600                && parent.named_child_count() == 1
6601                && parent.named_child(0) == Some(current));
6602        if wraps_current_declarator {
6603            match parent.kind() {
6604                "pointer_declarator" => indirection.push('*'),
6605                "reference_declarator" => {
6606                    let reference = parent
6607                        .children(&mut parent.walk())
6608                        .find(|child| !child.is_named())
6609                        .map(|child| node_text(child, source))
6610                        .unwrap_or("&");
6611                    indirection.push_str(reference);
6612                }
6613                "init_declarator" | "parenthesized_declarator" => {}
6614                _ => return None,
6615            }
6616            current = parent;
6617            continue;
6618        }
6619        return None;
6620    }
6621    None
6622}
6623
6624fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
6625    let mut required = 0;
6626    let mut total = 0;
6627    let mut repeated = false;
6628    let mut cursor = parameters_node.walk();
6629    for child in parameters_node.children(&mut cursor) {
6630        match child.kind() {
6631            "parameter_declaration" => {
6632                if child.child_by_field_name("declarator").is_none()
6633                    && child
6634                        .child_by_field_name("type")
6635                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
6636                {
6637                    continue;
6638                }
6639                required += 1;
6640                total += 1;
6641            }
6642            "optional_parameter_declaration" => total += 1,
6643            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6644                repeated = true;
6645            }
6646            _ => {}
6647        }
6648    }
6649    CallableArity::new(required, total, repeated)
6650}
6651
6652fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
6653    let mut labels = Vec::new();
6654    let mut cursor = parameters_node.walk();
6655    for child in parameters_node.children(&mut cursor) {
6656        match child.kind() {
6657            "parameter_declaration" | "optional_parameter_declaration" => {
6658                if let Some(name_node) = child
6659                    .child_by_field_name("declarator")
6660                    .and_then(cpp_declarator_label_node)
6661                {
6662                    labels.push(name_node);
6663                } else {
6664                    labels.push(child);
6665                }
6666            }
6667            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
6668                labels.push(child);
6669            }
6670            _ => {}
6671        }
6672    }
6673    labels
6674}
6675
6676fn cpp_signature_search_start(
6677    signature: &str,
6678    function_declarator: Node<'_>,
6679    source: &str,
6680) -> usize {
6681    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
6682        return 0;
6683    };
6684    let raw = node_text(enclosing, source);
6685    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
6686    let offset = function_declarator
6687        .start_byte()
6688        .saturating_sub(enclosing.start_byte())
6689        .saturating_sub(leading_trim_bytes);
6690    offset.min(signature.len())
6691}
6692
6693fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
6694    match node.kind() {
6695        "identifier" | "field_identifier" => Some(node),
6696        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
6697            .child_by_field_name("declarator")
6698            .or_else(|| last_named_child(node))
6699            .and_then(cpp_declarator_label_node),
6700        "array_declarator" => node
6701            .child_by_field_name("declarator")
6702            .and_then(cpp_declarator_label_node),
6703        "function_declarator" => node
6704            .child_by_field_name("declarator")
6705            .or_else(|| node.child_by_field_name("name"))
6706            .or_else(|| last_named_child(node))
6707            .and_then(cpp_declarator_label_node),
6708        _ => None,
6709    }
6710}
6711
6712fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
6713    let base_type = parameter
6714        .child_by_field_name("type")
6715        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
6716        .unwrap_or_default();
6717    let declarator = cpp_parameter_declarator(parameter);
6718    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
6719    // are discarded, so `f(const int)` and `f(int)` declare one function. A
6720    // qualifier written next to the parameter's type is only top-level when
6721    // the declarator adds no indirection; behind a pointer, reference or array
6722    // declarator the same qualifier belongs to the pointee, referent or
6723    // element and keeps distinguishing the type (#1827).
6724    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
6725    let mut cursor = parameter.walk();
6726    let qualifiers = parameter
6727        .named_children(&mut cursor)
6728        .filter(|child| child.kind() == "type_qualifier")
6729        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6730        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
6731        .collect::<Vec<_>>()
6732        .join(" ");
6733    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
6734        (true, _) => base_type,
6735        (_, true) => qualifiers,
6736        (false, false) => format!("{qualifiers} {base_type}"),
6737    };
6738    let declarator_suffix = declarator
6739        .map(|node| cpp_declarator_suffix_without_name(node, source))
6740        .unwrap_or_default();
6741
6742    let combined = if type_text.is_empty() {
6743        declarator_suffix
6744    } else if declarator_suffix.is_empty() {
6745        type_text
6746    } else {
6747        format!("{type_text} {declarator_suffix}")
6748    };
6749    normalize_cpp_type_text(&combined)
6750}
6751
6752fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
6753    parameter.child_by_field_name("declarator").or_else(|| {
6754        // Some unnamed prototype parameters expose their abstract declarator
6755        // as a direct named child without the grammar's `declarator` field.
6756        // Recover only the structured abstract-declarator node; the parameter's
6757        // type and qualifiers are distinct children and must not be guessed from
6758        // source text.
6759        let mut cursor = parameter.walk();
6760        parameter
6761            .named_children(&mut cursor)
6762            .find(|child| is_cpp_abstract_declarator(child.kind()))
6763    })
6764}
6765
6766/// Whether a parameter's declarator chain adds indirection - a pointer,
6767/// reference, array or function declarator - to the parameter's written type.
6768fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
6769    let mut current = Some(declarator);
6770    while let Some(node) = current {
6771        if matches!(
6772            node.kind(),
6773            "pointer_declarator"
6774                | "abstract_pointer_declarator"
6775                | "reference_declarator"
6776                | "abstract_reference_declarator"
6777                | "array_declarator"
6778                | "abstract_array_declarator"
6779                | "function_declarator"
6780                | "abstract_function_declarator"
6781        ) {
6782            return true;
6783        }
6784        current = cpp_nested_declarator(node);
6785    }
6786    false
6787}
6788
6789fn is_cpp_abstract_declarator(kind: &str) -> bool {
6790    matches!(
6791        kind,
6792        "abstract_pointer_declarator"
6793            | "abstract_reference_declarator"
6794            | "abstract_array_declarator"
6795            | "abstract_function_declarator"
6796            | "abstract_parenthesized_declarator"
6797    )
6798}
6799
6800fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
6801    node.child_by_field_name("declarator").or_else(|| {
6802        if is_cpp_abstract_declarator(node.kind()) {
6803            let mut cursor = node.walk();
6804            node.named_children(&mut cursor)
6805                .find(|child| is_cpp_abstract_declarator(child.kind()))
6806        } else {
6807            // Named declarators historically use their last named child when
6808            // tree-sitter omits the field. Keep that broad fallback for
6809            // attributed, variadic, and recovered named shapes.
6810            last_named_child(node)
6811        }
6812    })
6813}
6814
6815fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
6816    match node.kind() {
6817        "identifier" | "field_identifier" => String::new(),
6818        "pointer_declarator" | "abstract_pointer_declarator" => {
6819            let inner = cpp_nested_declarator(node)
6820                .map(|child| cpp_declarator_suffix_without_name(child, source))
6821                .unwrap_or_default();
6822            format!("*{inner}")
6823        }
6824        "reference_declarator" | "abstract_reference_declarator" => {
6825            let inner = cpp_nested_declarator(node)
6826                .map(|child| cpp_declarator_suffix_without_name(child, source))
6827                .unwrap_or_default();
6828            let reference = node
6829                .children(&mut node.walk())
6830                .find(|child| matches!(child.kind(), "&" | "&&"))
6831                .map(|child| node_text(child, source))
6832                .unwrap_or("&");
6833            format!("{reference}{inner}")
6834        }
6835        "array_declarator" | "abstract_array_declarator" => {
6836            let inner = cpp_nested_declarator(node)
6837                .map(|child| cpp_declarator_suffix_without_name(child, source))
6838                .unwrap_or_default();
6839            let size = node
6840                .child_by_field_name("size")
6841                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
6842                .unwrap_or_default();
6843            format!("{inner}[{size}]")
6844        }
6845        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
6846            let inner = cpp_nested_declarator(node);
6847            inner
6848                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
6849                .unwrap_or_default()
6850        }
6851        "function_declarator" | "abstract_function_declarator" => {
6852            let inner = cpp_nested_declarator(node)
6853                .map(|child| cpp_declarator_suffix_without_name(child, source))
6854                .unwrap_or_default();
6855            let params = node
6856                .child_by_field_name("parameters")
6857                .map(|child| cpp_parameter_signature(child, source))
6858                .unwrap_or_else(|| "()".to_string());
6859            format!("{inner}{params}")
6860        }
6861        _ => {
6862            let text = normalize_cpp_whitespace(node_text(node, source));
6863            let name = extract_declarator_name(node, source);
6864            if name.is_empty() {
6865                text
6866            } else {
6867                text.replace(&name, "").trim().to_string()
6868            }
6869        }
6870    }
6871}
6872
6873fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
6874    collapse_cpp_whitespace(
6875        suffix
6876            .trim()
6877            .trim_start_matches("->")
6878            .trim_start_matches('{')
6879            .trim_end_matches(';'),
6880    )
6881}
6882
6883pub fn normalize_cpp_whitespace(value: &str) -> String {
6884    collapse_cpp_whitespace(value)
6885}
6886
6887fn normalize_cpp_type_text(value: &str) -> String {
6888    collapse_cpp_whitespace(value)
6889        .replace(", ", ",")
6890        .replace(" <", "<")
6891        .replace("< ", "<")
6892        .replace(" >", ">")
6893}
6894
6895fn collapse_cpp_whitespace(value: &str) -> String {
6896    let mut result = String::new();
6897    let mut prev_space = false;
6898    for ch in value.chars() {
6899        if ch.is_whitespace() {
6900            if !prev_space {
6901                result.push(' ');
6902            }
6903            prev_space = true;
6904        } else {
6905            result.push(ch);
6906            prev_space = false;
6907        }
6908    }
6909    result.trim().to_string()
6910}
6911
6912pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
6913    node_source_text(node, source)
6914}
6915
6916pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
6917    walk_named_tree_preorder(node, true, |node| {
6918        match node.kind() {
6919            "type_identifier" | "identifier" | "qualified_identifier" => {
6920                let text = node_text(node, source).trim();
6921                if !text.is_empty() {
6922                    identifiers.insert(text.to_string());
6923                }
6924            }
6925            _ => {}
6926        }
6927        WalkControl::Continue
6928    });
6929}
6930
6931fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
6932    node.child_by_field_name("body").or_else(|| {
6933        let mut cursor = node.walk();
6934        node.named_children(&mut cursor).find(|child| {
6935            matches!(
6936                child.kind(),
6937                "declaration_list" | "field_declaration_list" | "enumerator_list"
6938            )
6939        })
6940    })
6941}
6942
6943/// Return a class body's actual closing brace when the parser supplied one.
6944///
6945/// A malformed namespace sentinel can leave a class node carrying unrelated
6946/// parser errors even though its own class body is complete.  `has_error()` is
6947/// therefore too coarse an admission predicate for sentinel ownership.  The
6948/// body list, however, exposes the opening and closing punctuation directly;
6949/// a real (non-missing) final `}` proves that the class did not borrow the
6950/// enclosing namespace's close.  Requiring the body to end before its parent
6951/// container also rejects a recovered node whose body swallowed that outer
6952/// boundary.
6953fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
6954    if !matches!(
6955        node.kind(),
6956        "class_specifier" | "struct_specifier" | "union_specifier"
6957    ) {
6958        return None;
6959    }
6960    let body = cpp_body_node(node)?;
6961    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
6962        return None;
6963    }
6964    let open = body.child(0)?;
6965    let close = body.child(body.child_count().checked_sub(1)?)?;
6966    if open.kind() != "{"
6967        || open.is_missing()
6968        || close.kind() != "}"
6969        || close.is_missing()
6970        || close.end_byte() != body.end_byte()
6971        || body.end_byte() > node.end_byte()
6972        || node
6973            .parent()
6974            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
6975    {
6976        return None;
6977    }
6978    Some(close)
6979}
6980
6981fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
6982    if node.kind() == "namespace_definition" {
6983        return true;
6984    }
6985    let mut cursor = node.walk();
6986    node.named_children(&mut cursor)
6987        .any(cpp_contains_namespace_definition)
6988}
6989
6990struct CppNestedNamespaceSentinel<'tree> {
6991    function: Node<'tree>,
6992    body: Node<'tree>,
6993    namespace_components: Vec<String>,
6994}
6995
6996/// Owned structural recovery metadata for a namespace-sentinel region.
6997///
6998/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
6999/// instead of the namespace/class scopes that the declaration visitor restores.
7000/// The inverted usage walk has the original CST, so it needs the same ownership
7001/// evidence without borrowing parser nodes across its file scan.  Keep this
7002/// descriptor deliberately source-range based: callers can match a reference
7003/// node by containment and then resolve its structured type spelling in the
7004/// recovered class scope.
7005#[derive(Debug, Clone)]
7006pub struct CppSentinelRecoveredOwner {
7007    pub range: Range,
7008    /// Start of the qualified owner name (`btree<P>::method`).  A leading
7009    /// return type before this byte is looked up from the namespace; parameters,
7010    /// trailing returns, and the body use the member owner scope.
7011    pub owner_name_start_byte: usize,
7012    /// Number of leading components belonging to the namespace rather than
7013    /// the qualified class owner.  A leading return type is looked up before
7014    /// every owner component, not merely before the innermost class.
7015    pub namespace_component_count: usize,
7016    pub scope_components: Vec<String>,
7017}
7018
7019#[derive(Debug, Clone)]
7020pub struct CppSentinelRecoveredClass {
7021    pub namespace_range: Range,
7022    pub namespace_scope_components: Vec<String>,
7023    pub class_range: Range,
7024    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
7025    pub scope_components: Vec<String>,
7026    /// Qualified out-of-line member definitions owned by this class.  Their
7027    /// ranges may extend beyond `class_range` when the malformed sentinel
7028    /// swallowed the namespace close and left definitions as function siblings.
7029    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
7030}
7031
7032/// Resolve the lexical scope restored for a node in a malformed
7033/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
7034/// outrank class spans, which in turn outrank the surviving namespace body.
7035/// The class ancestor suffix is recovered from the original CST so nested
7036/// members keep their complete `Outer::Inner` owner chain.
7037pub fn cpp_sentinel_recovered_scope_for_node(
7038    node: Node<'_>,
7039    source: &str,
7040    recovered_classes: &[CppSentinelRecoveredClass],
7041) -> Option<Vec<String>> {
7042    let contains =
7043        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
7044    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
7045    for recovered in recovered_classes {
7046        for owner in recovered
7047            .owner_ranges
7048            .iter()
7049            .filter(|owner| contains(owner.range))
7050        {
7051            let replace = best_owner.is_none_or(|existing| {
7052                owner.range.end_byte.saturating_sub(owner.range.start_byte)
7053                    < existing
7054                        .range
7055                        .end_byte
7056                        .saturating_sub(existing.range.start_byte)
7057            });
7058            if replace {
7059                best_owner = Some(owner);
7060            }
7061        }
7062    }
7063    if let Some(owner) = best_owner {
7064        let mut scope = owner.scope_components.clone();
7065        if node.start_byte() < owner.owner_name_start_byte {
7066            scope.truncate(owner.namespace_component_count);
7067        }
7068        return Some(scope);
7069    }
7070
7071    let class = recovered_classes
7072        .iter()
7073        .filter(|recovered| contains(recovered.class_range))
7074        .min_by_key(|recovered| {
7075            recovered
7076                .class_range
7077                .end_byte
7078                .saturating_sub(recovered.class_range.start_byte)
7079        });
7080    let class_scope = class.is_some();
7081    let mut scope = if let Some(class) = class {
7082        class.scope_components.clone()
7083    } else {
7084        let namespace = recovered_classes
7085            .iter()
7086            .filter(|recovered| contains(recovered.namespace_range))
7087            .min_by_key(|recovered| {
7088                recovered
7089                    .namespace_range
7090                    .end_byte
7091                    .saturating_sub(recovered.namespace_range.start_byte)
7092            })?;
7093        let mut scope = namespace.namespace_scope_components.clone();
7094        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
7095        let common_prefix = scope
7096            .iter()
7097            .zip(&parser_namespace)
7098            .take_while(|(recovered, parser)| recovered == parser)
7099            .count();
7100        scope.extend(parser_namespace.into_iter().skip(common_prefix));
7101        scope
7102    };
7103    if class_scope {
7104        let mut ancestor_components = Vec::new();
7105        let mut ancestor = node.parent();
7106        while let Some(current) = ancestor {
7107            if matches!(
7108                current.kind(),
7109                "class_specifier" | "struct_specifier" | "union_specifier"
7110            ) && let Some(name) = current.child_by_field_name("name")
7111                && let Some(name_components) = cpp_name_components(name, source)
7112            {
7113                ancestor_components.push(
7114                    name_components
7115                        .into_iter()
7116                        .map(|component| component.name)
7117                        .collect::<Vec<_>>(),
7118                );
7119            }
7120            ancestor = current.parent();
7121        }
7122        ancestor_components.reverse();
7123        let base_len = scope.len();
7124        for component in ancestor_components.into_iter().flatten() {
7125            if scope.len() >= base_len && scope.last() == Some(&component) {
7126                continue;
7127            }
7128            scope.push(component);
7129        }
7130    }
7131    Some(scope)
7132}
7133
7134struct CppSentinelFragmentedClassTail<'tree> {
7135    class_node: Node<'tree>,
7136    template_node: Option<Node<'tree>>,
7137    name: String,
7138    fragmented: FragmentedExportBody,
7139    consumed_start: usize,
7140}
7141
7142struct CppSentinelDirectBodyClassRegion {
7143    namespace_components: Vec<String>,
7144    class_start: usize,
7145    class_start_line: usize,
7146    class_close_end: usize,
7147    class_close_line: usize,
7148    name: String,
7149}
7150
7151fn cpp_sentinel_body_class_candidate<'tree>(
7152    child: Node<'tree>,
7153) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7154    if matches!(
7155        child.kind(),
7156        "class_specifier" | "struct_specifier" | "union_specifier"
7157    ) {
7158        return Some((child, None));
7159    }
7160    if child.kind() != "template_declaration" {
7161        if child.kind() == "declaration" {
7162            return Some((first_class_like_child(child)?, None));
7163        }
7164        return None;
7165    }
7166    let mut cursor = child.walk();
7167    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
7168        if matches!(
7169            candidate.kind(),
7170            "class_specifier" | "struct_specifier" | "union_specifier"
7171        ) {
7172            Some(candidate)
7173        } else if candidate.kind() == "declaration" {
7174            first_class_like_child(candidate)
7175        } else {
7176            None
7177        }
7178    })?;
7179    Some((class_node, Some(child)))
7180}
7181
7182fn cpp_sentinel_direct_body_class_candidate<'tree>(
7183    child: Node<'tree>,
7184) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
7185    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
7186        return Some(candidate);
7187    }
7188    if child.kind() != "template_declaration" {
7189        return None;
7190    }
7191    let mut cursor = child.walk();
7192    let wrapper = child
7193        .named_children(&mut cursor)
7194        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
7195    Some((first_class_like_child(wrapper)?, Some(child)))
7196}
7197
7198fn cpp_sentinel_direct_namespace_components(
7199    function: Node<'_>,
7200    body: Node<'_>,
7201    source: &str,
7202) -> Option<Vec<String>> {
7203    let mut cursor = function.walk();
7204    let children = function
7205        .named_children(&mut cursor)
7206        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
7207        .collect::<Vec<_>>();
7208    let sentinel_index = children.iter().rposition(|child| {
7209        direct_identifier_name(*child, source)
7210            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
7211    })?;
7212    let mut identifiers = Vec::new();
7213    let mut stack = children[sentinel_index + 1..]
7214        .iter()
7215        .rev()
7216        .copied()
7217        .collect::<Vec<_>>();
7218    while let Some(current) = stack.pop() {
7219        if let Some(name) = direct_identifier_name(current, source) {
7220            identifiers.push(name);
7221            continue;
7222        }
7223        let mut cursor = current.walk();
7224        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
7225        stack.extend(children.into_iter().rev());
7226    }
7227    let [keyword, namespace] = identifiers.as_slice() else {
7228        return None;
7229    };
7230    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
7231        .then(|| vec![namespace.clone()])
7232}
7233
7234fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
7235    let mut sibling = class_semicolon.next_named_sibling();
7236    let namespace_close = loop {
7237        let Some(current) = sibling else {
7238            return false;
7239        };
7240        sibling = current.next_named_sibling();
7241        if current.kind() != "comment" {
7242            break current;
7243        }
7244    };
7245    if !cpp_is_stray_close_brace(namespace_close, source) {
7246        return false;
7247    }
7248    loop {
7249        let Some(current) = sibling else {
7250            return false;
7251        };
7252        sibling = current.next_named_sibling();
7253        if current.kind() == "comment" {
7254            continue;
7255        }
7256        return direct_identifier_name(current, source)
7257            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
7258    }
7259}
7260
7261fn cpp_sentinel_macro_body_class_region(
7262    node: Node<'_>,
7263    source: &str,
7264) -> Option<CppSentinelDirectBodyClassRegion> {
7265    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
7266        return None;
7267    };
7268    if node.kind() != "function_definition" || !node.has_error() {
7269        return None;
7270    }
7271    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
7272    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
7273    let mut cursor = body.walk();
7274    let candidates = body
7275        .named_children(&mut cursor)
7276        .filter_map(cpp_sentinel_direct_body_class_candidate)
7277        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
7278        .collect::<Vec<_>>();
7279    let [(class_node, template_node)] = candidates.as_slice() else {
7280        return None;
7281    };
7282    let original_body = cpp_body_node(*class_node)?;
7283    let name = class_like_name(*class_node, source)?;
7284    if name.is_empty() || cpp_export_macro_token(&name) {
7285        return None;
7286    }
7287
7288    let mut sibling = node.next_named_sibling();
7289    let (class_close_start, class_close_end, class_close_line) = loop {
7290        let current = sibling?;
7291        let next = current.next_named_sibling();
7292        if cpp_is_stray_close_brace(current, source)
7293            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
7294        {
7295            let semicolon = next.expect("checked above");
7296            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
7297                return None;
7298            }
7299            break (
7300                current.start_byte(),
7301                semicolon.end_byte(),
7302                semicolon.end_position().row + 1,
7303            );
7304        }
7305        sibling = next;
7306    };
7307    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
7308    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
7309    let root = tree.root_node();
7310    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
7311    let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
7312    if reparsed.name != name
7313        || reparsed.declaration_node.start_byte() != class_node.start_byte()
7314        || reparsed.body.start_byte() != original_body.start_byte()
7315        || class_close_start <= reparsed.body.end_byte()
7316        || class_close_end <= class_node.end_byte()
7317    {
7318        return None;
7319    }
7320    Some(CppSentinelDirectBodyClassRegion {
7321        namespace_components,
7322        class_start: reparse_start,
7323        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
7324            node.start_position().row + 1
7325        }),
7326        class_close_end,
7327        class_close_line,
7328        name,
7329    })
7330}
7331
7332/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
7333/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
7334///
7335/// The parser puts the namespace opener and the malformed function in one root
7336/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
7337/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
7338/// the malformed function must begin with an all-caps type, then an ERROR whose
7339/// sole identifier is `namespace`, followed by the inner namespace identifier
7340/// and a compound body; and that body must contain complete named class
7341/// specifiers.  A text reparse cannot prove any of those ownership boundaries.
7342fn cpp_nested_namespace_sentinel<'tree>(
7343    node: Node<'tree>,
7344    source: &str,
7345) -> Option<CppNestedNamespaceSentinel<'tree>> {
7346    if !node.has_error() {
7347        return None;
7348    }
7349
7350    let (function, mut namespace_components) = if node.kind() == "ERROR" {
7351        let mut cursor = node.walk();
7352        let functions = node
7353            .named_children(&mut cursor)
7354            .filter(|child| child.kind() == "function_definition")
7355            .collect::<Vec<_>>();
7356        let [function] = functions.as_slice() else {
7357            return None;
7358        };
7359        if !function.has_error() {
7360            return None;
7361        }
7362        let mut cursor = node.walk();
7363        let children = node.children(&mut cursor).collect::<Vec<_>>();
7364        let function_index = children
7365            .iter()
7366            .position(|child| same_node(*child, *function))?;
7367        let [outer_keyword, outer_name, outer_open] =
7368            children.get(function_index.checked_sub(3)?..function_index)?
7369        else {
7370            return None;
7371        };
7372        if outer_keyword.kind() != "namespace"
7373            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
7374            || outer_open.kind() != "{"
7375        {
7376            return None;
7377        }
7378        (
7379            *function,
7380            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
7381        )
7382    } else if node.kind() == "function_definition" {
7383        let declaration_list = node.parent()?;
7384        let namespace = declaration_list.parent()?;
7385        if declaration_list.kind() != "declaration_list"
7386            || namespace.kind() != "namespace_definition"
7387            || namespace.child_by_field_name("body") != Some(declaration_list)
7388        {
7389            return None;
7390        }
7391        (node, Vec::new())
7392    } else {
7393        return None;
7394    };
7395
7396    let mut cursor = function.walk();
7397    let named = function
7398        .named_children(&mut cursor)
7399        .filter(|child| child.kind() != "comment")
7400        .collect::<Vec<_>>();
7401    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
7402        return None;
7403    };
7404    if first_type.kind() != "type_identifier" {
7405        return None;
7406    }
7407    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
7408    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
7409        return None;
7410    }
7411    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
7412        return None;
7413    }
7414    let inner_keyword = inner_error.named_child(0)?;
7415    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
7416        return None;
7417    }
7418    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
7419        return None;
7420    }
7421    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
7422    if inner_name.is_empty() || body.kind() != "compound_statement" {
7423        return None;
7424    }
7425    namespace_components.push(inner_name);
7426
7427    let mut cursor = body.walk();
7428    let classes = body
7429        .named_children(&mut cursor)
7430        .filter_map(cpp_sentinel_body_class_candidate)
7431        .filter(|(child, _)| {
7432            cpp_body_node(*child).is_some()
7433                && class_like_name(*child, source)
7434                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
7435        })
7436        .collect::<Vec<_>>();
7437    if classes.is_empty() {
7438        return None;
7439    }
7440
7441    Some(CppNestedNamespaceSentinel {
7442        function,
7443        body: *body,
7444        namespace_components,
7445    })
7446}
7447
7448/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
7449/// malformed namespace-sentinel function.  The recovery is deliberately
7450/// structural: the class must be a direct body item, its own class node must be
7451/// erroneous and end before a unique anonymous `}` in the enclosing
7452/// declaration-list, and that namespace's next sibling must be a standalone
7453/// `;`.  The complete interior must pass the existing member-shaped reparse
7454/// gate. This avoids source brace scans and does not borrow a close from an
7455/// unrelated later declaration.
7456fn cpp_sentinel_fragmented_class_tail<'tree>(
7457    function: Node<'tree>,
7458    body: Node<'tree>,
7459    source: &str,
7460) -> Option<CppSentinelFragmentedClassTail<'tree>> {
7461    let mut cursor = body.walk();
7462    let candidates = body
7463        .named_children(&mut cursor)
7464        .filter_map(cpp_sentinel_body_class_candidate)
7465        .filter(|(class_node, _)| cpp_body_node(*class_node).is_some() && class_node.has_error())
7466        .collect::<Vec<_>>();
7467    let [(class_node, template_node)] = candidates.as_slice() else {
7468        return None;
7469    };
7470    let name = class_like_name(*class_node, source)?;
7471    if name.is_empty() || cpp_export_macro_token(&name) {
7472        return None;
7473    }
7474    let class_body = cpp_body_node(*class_node)?;
7475
7476    let (close, semicolon) =
7477        cpp_sentinel_fragment_boundary(function, *class_node, class_body, source)?;
7478
7479    let reparse_start = class_body.start_byte().checked_add(1)?;
7480    let reparse_end = close.start_byte();
7481    if reparse_start >= reparse_end {
7482        return None;
7483    }
7484    let tree = cpp_reparse_region_items(source, reparse_start, reparse_end)?;
7485    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
7486        return None;
7487    }
7488    let class_range = Range {
7489        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7490        end_byte: semicolon.end_byte(),
7491        start_line: template_node.map_or(class_node.start_position().row, |node| {
7492            node.start_position().row
7493        }) + 1,
7494        end_line: semicolon.end_position().row + 1,
7495    };
7496    Some(CppSentinelFragmentedClassTail {
7497        class_node: *class_node,
7498        template_node: *template_node,
7499        name,
7500        fragmented: FragmentedExportBody {
7501            reparse_start,
7502            reparse_end,
7503            class_range,
7504        },
7505        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
7506    })
7507}
7508
7509/// Recover the class and out-of-line owner scopes from every malformed
7510/// namespace-sentinel region in `root`.
7511///
7512/// This is the shared structural counterpart to
7513/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
7514/// reuses the visitor's sentinel/class admission predicates instead of parsing
7515/// source text a second time.  The returned values own only ranges and names, so
7516/// they can be retained by an inverted usage scan after the tree borrow ends.
7517pub fn cpp_sentinel_recovered_classes(
7518    root: Node<'_>,
7519    source: &str,
7520) -> Vec<CppSentinelRecoveredClass> {
7521    if !root.has_error() {
7522        return Vec::new();
7523    }
7524    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
7525    let mut stack = vec![root];
7526    while let Some(current) = stack.pop() {
7527        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source) {
7528            let namespace_components = cpp_sentinel_recovered_namespace_components(
7529                recovered.function,
7530                &recovered.namespace_components,
7531                source,
7532            );
7533            let fragmented =
7534                cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
7535            let mut class_candidates = Vec::new();
7536            let mut cursor = recovered.body.walk();
7537            for (class_node, template_node) in recovered
7538                .body
7539                .named_children(&mut cursor)
7540                .filter_map(cpp_sentinel_body_class_candidate)
7541            {
7542                let Some(name) = class_like_name(class_node, source) else {
7543                    continue;
7544                };
7545                if name.is_empty() || cpp_export_macro_token(&name) {
7546                    continue;
7547                }
7548                let is_fragmented = fragmented
7549                    .as_ref()
7550                    .is_some_and(|tail| same_node(tail.class_node, class_node));
7551                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
7552                    continue;
7553                }
7554                let class_range = if is_fragmented {
7555                    fragmented
7556                        .as_ref()
7557                        .map(|tail| tail.fragmented.class_range)
7558                        .expect("fragmented class range is present when class matches")
7559                } else {
7560                    cpp_declaration_range(template_node.unwrap_or(class_node))
7561                };
7562                class_candidates.push((class_range, name));
7563            }
7564
7565            let mut owner_ranges =
7566                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
7567            cpp_sentinel_extend_unique_owner_ranges(
7568                &mut owner_ranges,
7569                cpp_sentinel_recovered_sibling_owner_ranges(
7570                    recovered.function,
7571                    &namespace_components,
7572                    source,
7573                ),
7574            );
7575            for (class_range, name) in class_candidates {
7576                push_cpp_sentinel_recovered_class(
7577                    &mut recovered_classes,
7578                    cpp_declaration_range(recovered.body),
7579                    &namespace_components,
7580                    class_range,
7581                    name,
7582                    &owner_ranges,
7583                );
7584            }
7585
7586            if let Some(declaration_list) = recovered
7587                .function
7588                .parent()
7589                .filter(|parent| parent.kind() == "declaration_list")
7590            {
7591                let outer_namespace =
7592                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
7593                push_cpp_sentinel_sibling_classes(
7594                    &mut recovered_classes,
7595                    declaration_list,
7596                    recovered.function,
7597                    &outer_namespace,
7598                    source,
7599                );
7600            }
7601        } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
7602            let namespace_components = cpp_sentinel_recovered_namespace_components(
7603                current,
7604                &region.namespace_components,
7605                source,
7606            );
7607            let owner_container = current
7608                .parent()
7609                .filter(|parent| parent.kind() == "declaration_list")
7610                .unwrap_or(current);
7611            let owner_ranges =
7612                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7613            push_cpp_sentinel_recovered_class(
7614                &mut recovered_classes,
7615                cpp_declaration_range(owner_container),
7616                &namespace_components,
7617                Range {
7618                    start_byte: region.class_start,
7619                    end_byte: region.class_close_end,
7620                    start_line: region.class_start_line,
7621                    end_line: region.class_close_line,
7622                },
7623                region.name,
7624                &owner_ranges,
7625            );
7626        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
7627            // A generic sentinel-prefixed class can be reduced as a malformed
7628            // function/ERROR without the explicit `namespace X` token pair.
7629            // Reuse the declaration visitor's bounded reparse and retain only
7630            // the recovered class identity/range here.
7631            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
7632                region;
7633            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
7634                continue;
7635            };
7636            let root = tree.root_node();
7637            let template_node = cpp_sentinel_reparsed_leading_template(root);
7638            let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
7639            else {
7640                continue;
7641            };
7642            let class_node = reparsed_class.declaration_node;
7643            let name = reparsed_class.name;
7644            let namespace_components =
7645                cpp_sentinel_recovered_namespace_components(current, &[], source);
7646            let owner_container = current
7647                .parent()
7648                .filter(|parent| parent.kind() == "declaration_list")
7649                .unwrap_or(current);
7650            let mut owner_ranges =
7651                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
7652            cpp_sentinel_extend_unique_owner_ranges(
7653                &mut owner_ranges,
7654                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
7655            );
7656            push_cpp_sentinel_recovered_class(
7657                &mut recovered_classes,
7658                cpp_declaration_range(owner_container),
7659                &namespace_components,
7660                Range {
7661                    start_byte: class_start,
7662                    end_byte: close_end,
7663                    start_line: class_node.start_position().row + 1,
7664                    end_line: class_node.end_position().row + 1,
7665                },
7666                name,
7667                &owner_ranges,
7668            );
7669            if owner_container.kind() == "declaration_list" {
7670                push_cpp_sentinel_sibling_classes(
7671                    &mut recovered_classes,
7672                    owner_container,
7673                    current,
7674                    &namespace_components,
7675                    source,
7676                );
7677            }
7678        }
7679
7680        let mut cursor = current.walk();
7681        stack.extend(current.named_children(&mut cursor));
7682    }
7683    // A shallower sentinel can expose nested classes as apparent namespace
7684    // siblings even after a deeper sentinel proves that a containing class
7685    // owns their ranges. Drop those shadow descriptors; scope recovery starts
7686    // from the proven containing class and appends parser-visible class
7687    // ancestors, preserving the full `Outer::Inner` chain.
7688    let shadowed = recovered_classes
7689        .iter()
7690        .map(|candidate| {
7691            recovered_classes.iter().any(|container| {
7692                container.class_range.start_byte <= candidate.class_range.start_byte
7693                    && container.class_range.end_byte >= candidate.class_range.end_byte
7694                    && container.class_range != candidate.class_range
7695                    && container.namespace_scope_components.len()
7696                        > candidate.namespace_scope_components.len()
7697                    && container
7698                        .namespace_scope_components
7699                        .starts_with(&candidate.namespace_scope_components)
7700            })
7701        })
7702        .collect::<Vec<_>>();
7703    let mut index = 0usize;
7704    recovered_classes.retain(|_| {
7705        let keep = !shadowed[index];
7706        index += 1;
7707        keep
7708    });
7709    recovered_classes
7710}
7711
7712/// A flat sentinel can swallow the first class while leaving later classes and
7713/// their out-of-line definitions as ordinary declaration-list siblings.  Once
7714/// the malformed class proves the sentinel envelope, retain those structurally
7715/// complete sibling classes under the same surviving namespace so every member
7716/// owner in the region uses one recovery contract.
7717fn push_cpp_sentinel_sibling_classes(
7718    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7719    declaration_list: Node<'_>,
7720    sentinel_node: Node<'_>,
7721    namespace_components: &[String],
7722    source: &str,
7723) {
7724    let owner_ranges =
7725        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
7726    let namespace_range = cpp_declaration_range(declaration_list);
7727    let mut cursor = declaration_list.walk();
7728    for (class_node, template_node) in declaration_list
7729        .named_children(&mut cursor)
7730        .filter(|child| !same_node(*child, sentinel_node))
7731        .filter_map(cpp_sentinel_body_class_candidate)
7732    {
7733        let Some(name) = class_like_name(class_node, source) else {
7734            continue;
7735        };
7736        if name.is_empty()
7737            || cpp_export_macro_token(&name)
7738            || cpp_complete_class_body_close(class_node).is_none()
7739        {
7740            continue;
7741        }
7742        push_cpp_sentinel_recovered_class(
7743            recovered_classes,
7744            namespace_range,
7745            namespace_components,
7746            cpp_declaration_range(template_node.unwrap_or(class_node)),
7747            name,
7748            &owner_ranges,
7749        );
7750    }
7751}
7752
7753fn push_cpp_sentinel_recovered_class(
7754    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
7755    namespace_range: Range,
7756    namespace_components: &[String],
7757    class_range: Range,
7758    name: String,
7759    owner_ranges: &[CppSentinelRecoveredOwner],
7760) {
7761    let mut scope_components = namespace_components.to_vec();
7762    scope_components.push(name);
7763    let owner_ranges = owner_ranges
7764        .iter()
7765        .filter(|owner| owner.scope_components.starts_with(&scope_components))
7766        .cloned()
7767        .collect::<Vec<_>>();
7768    if recovered_classes.iter().any(|existing| {
7769        existing.class_range == class_range && existing.scope_components == scope_components
7770    }) {
7771        return;
7772    }
7773    recovered_classes.push(CppSentinelRecoveredClass {
7774        namespace_range,
7775        namespace_scope_components: namespace_components.to_vec(),
7776        class_range,
7777        scope_components,
7778        owner_ranges,
7779    });
7780}
7781
7782fn cpp_sentinel_recovered_namespace_components(
7783    function: Node<'_>,
7784    recovered_components: &[String],
7785    source: &str,
7786) -> Vec<String> {
7787    let mut ancestor_components = Vec::new();
7788    let mut ancestor = function.parent();
7789    while let Some(current) = ancestor {
7790        if current.kind() == "namespace_definition"
7791            && let Some(name_node) = current.child_by_field_name("name")
7792            && let Some(components) = cpp_name_components(name_node, source)
7793        {
7794            ancestor_components.push(
7795                components
7796                    .into_iter()
7797                    .map(|component| component.name)
7798                    .collect::<Vec<_>>(),
7799            );
7800        }
7801        ancestor = current.parent();
7802    }
7803    ancestor_components.reverse();
7804    let mut ancestors = ancestor_components
7805        .into_iter()
7806        .flatten()
7807        .collect::<Vec<_>>();
7808
7809    let overlap = (0..=ancestors.len().min(recovered_components.len()))
7810        .rev()
7811        .find(|length| {
7812            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
7813        })
7814        .unwrap_or(0);
7815    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
7816    ancestors
7817}
7818
7819fn cpp_sentinel_recovered_owner_ranges(
7820    body: Node<'_>,
7821    namespace_components: &[String],
7822    source: &str,
7823) -> Vec<CppSentinelRecoveredOwner> {
7824    let mut owners = Vec::new();
7825    walk_named_tree_preorder(body, true, |node| {
7826        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7827    });
7828    owners
7829}
7830
7831fn cpp_sentinel_collect_owner_range(
7832    node: Node<'_>,
7833    namespace_components: &[String],
7834    source: &str,
7835    owners: &mut Vec<CppSentinelRecoveredOwner>,
7836) -> WalkControl {
7837    if node.kind() != "function_definition" {
7838        return WalkControl::Continue;
7839    }
7840    let Some(function_declarator) = extract_function_declarator(node) else {
7841        return WalkControl::Continue;
7842    };
7843    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
7844        return WalkControl::Continue;
7845    };
7846    let Some(mut components) = cpp_name_components(name_node, source) else {
7847        return WalkControl::Continue;
7848    };
7849    if components.len() <= 1 {
7850        return WalkControl::Continue;
7851    }
7852    components.pop();
7853    let mut owner_components = components
7854        .into_iter()
7855        .map(|component| component.name)
7856        .collect::<Vec<_>>();
7857    let overlap = (0..=namespace_components.len().min(owner_components.len()))
7858        .rev()
7859        .find(|length| {
7860            owner_components[..*length]
7861                == namespace_components[namespace_components.len().saturating_sub(*length)..]
7862        })
7863        .unwrap_or(0);
7864    let mut scope_components = namespace_components.to_vec();
7865    scope_components.extend(owner_components.drain(overlap..));
7866    if scope_components.len() <= namespace_components.len() {
7867        return WalkControl::Continue;
7868    }
7869    let range = cpp_declaration_range(node);
7870    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
7871        existing.range == range && existing.scope_components == scope_components
7872    }) {
7873        owners.push(CppSentinelRecoveredOwner {
7874            range,
7875            owner_name_start_byte: name_node.start_byte(),
7876            namespace_component_count: namespace_components.len(),
7877            scope_components,
7878        });
7879    }
7880    WalkControl::Continue
7881}
7882
7883fn cpp_sentinel_extend_unique_owner_ranges(
7884    owners: &mut Vec<CppSentinelRecoveredOwner>,
7885    additional: Vec<CppSentinelRecoveredOwner>,
7886) {
7887    for owner in additional {
7888        if !owners.iter().any(|existing| {
7889            existing.range == owner.range && existing.scope_components == owner.scope_components
7890        }) {
7891            owners.push(owner);
7892        }
7893    }
7894}
7895
7896fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
7897    if node.kind() != "ERROR" || node.named_child_count() != 1 {
7898        return false;
7899    }
7900    let Some(end_name) = node.named_child(0) else {
7901        return false;
7902    };
7903    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
7904        return false;
7905    }
7906    let mut cursor = node.walk();
7907    node.children(&mut cursor)
7908        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
7909}
7910
7911/// Collect owner definitions that the malformed sentinel left as later
7912/// declaration-list siblings. Parser-visible namespace siblings are a hard
7913/// boundary: their declarations must keep their own lexical namespace.
7914fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7915    parent: Node<'_>,
7916    sentinel_node: Node<'_>,
7917    namespace_components: &[String],
7918    source: &str,
7919) -> Vec<CppSentinelRecoveredOwner> {
7920    let mut owners = Vec::new();
7921    let mut after_sentinel = false;
7922    let mut cursor = parent.walk();
7923    for child in parent.named_children(&mut cursor) {
7924        if !after_sentinel {
7925            if same_node(child, sentinel_node) {
7926                after_sentinel = true;
7927            }
7928            continue;
7929        }
7930        walk_named_tree_preorder(child, true, |node| {
7931            if node.kind() == "namespace_definition" {
7932                return WalkControl::SkipChildren;
7933            }
7934            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7935        });
7936    }
7937    owners
7938}
7939
7940/// Collect owner definitions after a malformed namespace, stopping only at
7941/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
7942/// enclosing container is not trusted to belong to the recovered namespace.
7943fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
7944    parent: Node<'_>,
7945    sentinel_node: Node<'_>,
7946    namespace_components: &[String],
7947    source: &str,
7948) -> Option<Vec<CppSentinelRecoveredOwner>> {
7949    let mut owners = Vec::new();
7950    let mut after_namespace = false;
7951    let mut cursor = parent.walk();
7952    for child in parent.named_children(&mut cursor) {
7953        if !after_namespace {
7954            if same_node(child, sentinel_node) {
7955                after_namespace = true;
7956            }
7957            continue;
7958        }
7959        if cpp_sentinel_namespace_end(child, source) {
7960            return Some(owners);
7961        }
7962        walk_named_tree_preorder(child, true, |node| {
7963            if node.kind() == "namespace_definition" {
7964                return WalkControl::SkipChildren;
7965            }
7966            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
7967        });
7968    }
7969    None
7970}
7971
7972fn cpp_sentinel_recovered_sibling_owner_ranges(
7973    sentinel_node: Node<'_>,
7974    namespace_components: &[String],
7975    source: &str,
7976) -> Vec<CppSentinelRecoveredOwner> {
7977    let Some(declaration_list) = sentinel_node
7978        .parent()
7979        .filter(|parent| parent.kind() == "declaration_list")
7980    else {
7981        return Vec::new();
7982    };
7983    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
7984        declaration_list,
7985        sentinel_node,
7986        namespace_components,
7987        source,
7988    );
7989
7990    let Some(namespace) = declaration_list
7991        .parent()
7992        .filter(|parent| parent.kind() == "namespace_definition")
7993    else {
7994        return owners;
7995    };
7996    let Some(outer_parent) = namespace.parent() else {
7997        return owners;
7998    };
7999    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
8000        outer_parent,
8001        namespace,
8002        namespace_components,
8003        source,
8004    ) {
8005        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
8006    }
8007    owners
8008}
8009
8010fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
8011    let mut current = function_declarator.child_by_field_name("declarator")?;
8012    loop {
8013        if matches!(
8014            current.kind(),
8015            "qualified_identifier"
8016                | "scoped_identifier"
8017                | "scoped_type_identifier"
8018                | "identifier"
8019                | "field_identifier"
8020                | "operator_name"
8021                | "destructor_name"
8022                | "literal_operator_name"
8023        ) {
8024            return Some(current);
8025        }
8026        current = current
8027            .child_by_field_name("declarator")
8028            .or_else(|| current.child_by_field_name("name"))
8029            .or_else(|| last_named_child(current))?;
8030    }
8031}
8032
8033fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
8034    match node.kind() {
8035        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8036            let mut components = match node.child_by_field_name("scope") {
8037                Some(scope) => cpp_name_components(scope, source)?,
8038                None => Vec::new(),
8039            };
8040            let name = node.child_by_field_name("name")?;
8041            components.push(canonical_cpp_qualified_component(name, source)?);
8042            Some(components)
8043        }
8044        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
8045    }
8046}
8047
8048fn cpp_sentinel_fragment_boundary<'tree>(
8049    function: Node<'tree>,
8050    class_node: Node<'tree>,
8051    class_body: Node<'tree>,
8052    source: &str,
8053) -> Option<(Node<'tree>, Node<'tree>)> {
8054    let declaration_list = function.parent()?;
8055    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
8056        return None;
8057    }
8058    let namespace = declaration_list.parent()?;
8059    if namespace.kind() != "namespace_definition"
8060        || namespace.child_by_field_name("body") != Some(declaration_list)
8061    {
8062        return None;
8063    }
8064    let mut cursor = declaration_list.walk();
8065    let closes = declaration_list
8066        .children(&mut cursor)
8067        .filter(|child| {
8068            !child.is_named()
8069                && child.kind() == "}"
8070                && child.start_byte() >= function.end_byte()
8071                && child.start_byte() > class_node.end_byte()
8072                && child.start_byte() > class_body.start_byte()
8073        })
8074        .collect::<Vec<_>>();
8075    let [close] = closes.as_slice() else {
8076        return None;
8077    };
8078    let semicolon = namespace.next_named_sibling()?;
8079    if !cpp_is_stray_semicolon(semicolon, source)
8080        || close.end_byte() != namespace.end_byte()
8081        || semicolon.start_byte() < namespace.end_byte()
8082    {
8083        return None;
8084    }
8085    Some((*close, semicolon))
8086}
8087
8088/// Detect the bogus declaration/function tree that tree-sitter recovers for a
8089/// region prefixed by an object-like macro sentinel the parser cannot see
8090/// (issue #941), and return the byte range `[start, end)` of the swallowed
8091/// declaration interior to reparse.
8092///
8093/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
8094/// `function_definition` whose first non-comment named child is the sentinel
8095/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
8096/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
8097/// the real items.
8098/// `start` is the end of the sentinel identifier -- everything after it is the
8099/// genuine source. `end` is the node's end, extended across any trailing empty
8100/// `;` statement the mis-parse displaced past the node (the class/struct closing
8101/// semicolon), so the reparse sees a complete, brace-balanced item.
8102///
8103/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
8104/// node (`has_error`). Unknown annotation/export macros can make a real callable
8105/// error-recovered even though tree-sitter still preserves its declarator, so a
8106/// preserved callable is admitted only when a displaced class keyword precedes
8107/// that declarator. The clean-reparse-to-items gate in
8108/// `cpp_reparsed_items_are_indexable` is the final arbiter.
8109/// Return the reparse start and, when present, the structurally recovered class
8110/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
8111/// separately from the reparse start because an opaque template-declaration
8112/// macro may precede it.
8113fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
8114    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
8115    {
8116        return None;
8117    }
8118    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
8119    // retain a valid function declarator despite the unknown export macro making
8120    // the outer node erroneous. Remember that declarator for the ordering gate
8121    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
8122    // class keyword precedes a spurious callable assembled from a later member.
8123    let mut declarator_cursor = node.walk();
8124    let preserved_callable = node
8125        .children_by_field_name("declarator", &mut declarator_cursor)
8126        .find_map(extract_function_declarator);
8127    // Leading documentation comments are attached to the malformed
8128    // `function_definition` as named children.  They are not part of the
8129    // sentinel prefix, so select the first non-comment child structurally
8130    // rather than requiring the sentinel to be child zero.  This is the shape
8131    // emitted for nlohmann/json's `basic_json`: its class documentation comment
8132    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
8133    // envelope otherwise ends at the first nested union.
8134    let mut cursor = node.walk();
8135    let first = node
8136        .named_children(&mut cursor)
8137        .find(|child| child.kind() != "comment")?;
8138    if first.kind() != "type_identifier" {
8139        return None;
8140    }
8141    let sentinel = normalize_cpp_whitespace(node_text(first, source));
8142    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8143        return None;
8144    }
8145    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
8146    // makes the trailing sentinel of one region and the leading sentinel of the
8147    // next both land as bare macro-token identifiers ahead of the real content.
8148    // Advance past every leading macro-token identifier so the reparse begins at
8149    // genuine source rather than another sentinel that would re-form the bogus
8150    // shape and fail the reparse gate.
8151    let mut start = first.end_byte();
8152    let mut after_first = false;
8153    let mut cursor = node.walk();
8154    for child in node.named_children(&mut cursor) {
8155        if !after_first {
8156            if same_node(child, first) {
8157                after_first = true;
8158            }
8159            continue;
8160        }
8161        if matches!(child.kind(), "identifier" | "type_identifier")
8162            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
8163        {
8164            start = child.end_byte();
8165        } else {
8166            break;
8167        }
8168    }
8169    // An additional opaque template-declaration macro before a class can be
8170    // folded into the bogus function's qualified declarator.  In that shape
8171    // the macro is not a direct sibling we can skip above; tree-sitter exposes
8172    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
8173    // Reparse from that keyword (or a real preceding `template` keyword) so the
8174    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
8175    // a class nested in a genuine sentinel-wrapped namespace lies after the
8176    // body opening and must not change the established region start.
8177    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
8178    let mut class_start = None;
8179    let mut template_start = None;
8180    let mut stack = vec![node];
8181    while let Some(current) = stack.pop() {
8182        if current.start_byte() >= prefix_end {
8183            continue;
8184        }
8185        if matches!(
8186            current.kind(),
8187            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
8188        ) {
8189            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
8190                "class" | "struct" | "union" | "enum" => {
8191                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
8192                        seen.min(current.start_byte())
8193                    }));
8194                }
8195                "template" => {
8196                    template_start =
8197                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
8198                            seen.min(current.start_byte())
8199                        }));
8200                }
8201                _ => {}
8202            }
8203        }
8204        let mut cursor = current.walk();
8205        stack.extend(current.children(&mut cursor));
8206    }
8207    if preserved_callable.is_some_and(|callable| {
8208        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
8209    }) {
8210        return None;
8211    }
8212    if let Some(class_start) = class_start {
8213        start = template_start
8214            .filter(|template_start| *template_start < class_start)
8215            .unwrap_or(class_start);
8216    }
8217    Some((start, class_start))
8218}
8219
8220/// Locate a sentinel-prefixed class whose malformed declaration was split across
8221/// root-level siblings. The true class close is represented structurally as a
8222/// lone `}` error followed by the class's displaced `;`; nested method/body
8223/// errors are not direct siblings of the sentinel node and therefore cannot
8224/// satisfy this pair.
8225fn cpp_sentinel_macro_class_region(
8226    node: Node<'_>,
8227    source: &str,
8228) -> Option<(usize, usize, usize, usize, usize, usize)> {
8229    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
8230        return None;
8231    };
8232    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
8233        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
8234        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
8235    if class_start >= body_open_start {
8236        return None;
8237    }
8238    let sibling_close = {
8239        let mut sibling = node.next_named_sibling();
8240        let mut found = None;
8241        while let Some(current) = sibling {
8242            let next = current.next_named_sibling();
8243            if cpp_is_stray_close_brace(current, source)
8244                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8245            {
8246                let semicolon = next.expect("checked above");
8247                found = Some((
8248                    current.start_byte(),
8249                    semicolon.end_byte(),
8250                    semicolon.end_position().row + 1,
8251                ));
8252                break;
8253            }
8254            sibling = next;
8255        }
8256        found
8257    };
8258    let (class_close_start, class_close_end, class_close_line) =
8259        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
8260            (class_close_start, class_close_end, class_close_line)
8261        } else {
8262            // When the malformed envelope itself is an ERROR, tree-sitter can
8263            // leave the class's balanced close in the source while promoting
8264            // all following members to siblings. Reparse the complete suffix
8265            // and use the first body-bearing class node's own field range as
8266            // the partition boundary. This keeps balancing in tree-sitter and
8267            // preserves the source's original byte offsets.
8268            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
8269            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
8270            let reparsed_class =
8271                cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
8272            let body = reparsed_class.body;
8273            let class_close_end = body.end_byte();
8274            let class_close_start = class_close_end.checked_sub(1)?;
8275            let class_close_line = body.end_position().row + 1;
8276            (class_close_start, class_close_end, class_close_line)
8277        };
8278    if class_close_start <= class_start {
8279        return None;
8280    }
8281
8282    // Reparse only far enough to expose the class body opening. This is a
8283    // structured check that the candidate really begins with a body-bearing
8284    // class-like item; the original malformed tree cannot provide that node.
8285    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8286    let class_root = tree.root_node();
8287    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
8288    let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
8289    let body = reparsed_class.body;
8290    // The class body opening must agree with the malformed wrapper's structured
8291    // body field. This rejects an inner nested class while permitting later
8292    // members to remain fragmented as root-level siblings in the bounded parse.
8293    if body.start_byte() != body_open_start {
8294        return None;
8295    }
8296    let body_start = body.start_byte().checked_add(1)?;
8297    (body_start < class_close_start).then_some((
8298        reparse_start,
8299        class_start,
8300        body_start,
8301        class_close_start,
8302        class_close_end,
8303        class_close_line,
8304    ))
8305}
8306
8307/// Find the `{` token immediately following the class/struct/union/enum token
8308/// at `class_start` in the malformed tree. The token is anonymous in the C++
8309/// grammar, so this deliberately walks all children (not only named children)
8310/// and relies on sibling structure rather than source-text searching.
8311fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
8312    let mut stack = vec![node];
8313    while let Some(current) = stack.pop() {
8314        if current.start_byte() == class_start
8315            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
8316        {
8317            let mut sibling = current.next_sibling();
8318            while let Some(candidate) = sibling {
8319                if candidate.kind() == "{" {
8320                    return Some(candidate.start_byte());
8321                }
8322                sibling = candidate.next_sibling();
8323            }
8324        }
8325        let mut cursor = current.walk();
8326        stack.extend(current.children(&mut cursor));
8327    }
8328    None
8329}
8330
8331/// The class body that tree-sitter displaced out of a sentinel-prefixed
8332/// declaration and left as the malformed node's next sibling.
8333///
8334/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
8335/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
8336/// the last child of that `ERROR` and its `{` opens a sibling
8337/// `compound_statement` instead. The body is still the malformed tree's own
8338/// structured token, which is what the caller's `body.start_byte() !=
8339/// body_open_start` agreement check needs; it just is not reachable by walking
8340/// forward from the class token inside the node.
8341fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
8342    node.next_named_sibling()
8343        .filter(|sibling| sibling.kind() == "compound_statement")
8344}
8345
8346fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
8347    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
8348    let mut end = if class_start.is_some() {
8349        cpp_macro_prefixed_class_end(source, start)?
8350    } else {
8351        node.end_byte()
8352    };
8353    if class_start.is_none()
8354        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
8355    {
8356        end = end.max(namespace_end);
8357    }
8358    let mut sibling = node.next_named_sibling();
8359    while let Some(current) = sibling {
8360        if !cpp_is_stray_semicolon(current, source) {
8361            break;
8362        }
8363        end = current.end_byte();
8364        sibling = current.next_named_sibling();
8365    }
8366    (start < end).then_some((start, end))
8367}
8368
8369/// Extend a sentinel reparse through a following namespace that tree-sitter
8370/// flattened into the sentinel node's sibling list.
8371///
8372/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
8373/// unknown macro becomes a false function return type and consumes the first
8374/// namespace body. A second `namespace detail` then loses its enclosing node:
8375/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
8376/// attaches its declarations to the surrounding error tree. Reparse from that
8377/// structured keyword so tree-sitter, rather than a source-text brace scan,
8378/// supplies the complete namespace boundary.
8379fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
8380    let mut sibling = node.next_sibling();
8381    let keyword = loop {
8382        let candidate = sibling?;
8383        sibling = candidate.next_sibling();
8384        if candidate.kind() != "comment" {
8385            break candidate;
8386        }
8387    };
8388    if keyword.kind() != "namespace" {
8389        return None;
8390    }
8391    let name = loop {
8392        let candidate = sibling?;
8393        sibling = candidate.next_sibling();
8394        if candidate.kind() != "comment" {
8395            break candidate;
8396        }
8397    };
8398    if cpp_namespace_name_components(name, source).is_empty() {
8399        return None;
8400    }
8401    let open = loop {
8402        let candidate = sibling?;
8403        sibling = candidate.next_sibling();
8404        if candidate.kind() != "comment" {
8405            break candidate;
8406        }
8407    };
8408    if open.kind() != "{" {
8409        return None;
8410    }
8411
8412    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
8413    let root = tree.root_node();
8414    let mut cursor = root.walk();
8415    let namespace = root
8416        .named_children(&mut cursor)
8417        .find(|candidate| candidate.kind() != "comment")?;
8418    (namespace.kind() == "namespace_definition"
8419        && namespace.start_byte() == keyword.start_byte()
8420        && namespace.child_by_field_name("body").is_some())
8421    .then_some(namespace.end_byte())
8422}
8423
8424/// Parse the source suffix beginning at a structurally recovered class/template
8425/// keyword and return the end of its first body-bearing class item.  The parser,
8426/// rather than a brace scanner, owns nested-body balancing.  This is needed when
8427/// the original error tree truncates the class and scatters later members as
8428/// top-level siblings.
8429fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
8430    let tree = cpp_reparse_region_items(source, start, source.len())?;
8431    let root = tree.root_node();
8432    let mut cursor = root.walk();
8433    for item in root.named_children(&mut cursor) {
8434        if item.end_byte() <= start || item.kind() == "comment" {
8435            continue;
8436        }
8437        let mut stack = vec![item];
8438        while let Some(current) = stack.pop() {
8439            if matches!(
8440                current.kind(),
8441                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
8442            ) && cpp_body_node(current).is_some()
8443            {
8444                return Some(current.end_byte());
8445            }
8446            let mut cursor = current.walk();
8447            stack.extend(current.named_children(&mut cursor));
8448        }
8449        // The recovered prefix is required to begin with the class item.  If
8450        // the first real item is something else, fail closed rather than skip
8451        // arbitrary source looking for a later class.
8452        return None;
8453    }
8454    None
8455}
8456
8457/// An empty `;` statement: the displaced closing semicolon of a struct/class that
8458/// the sentinel mis-parse split off past the bogus function node.
8459fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
8460    node.kind() == "expression_statement"
8461        && node.named_child_count() == 0
8462        && node_text(node, source).trim() == ";"
8463}
8464
8465/// Recover the real field name when a leading object-like annotation macro
8466/// displaces a qualified type into tree-sitter's bit-field recovery shape.
8467///
8468/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
8469/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
8470/// `bitfield_clause` containing an error plus an assignment.  The assignment's
8471/// left field is the only structured declaration name in that malformed tail.
8472/// A real bit-field is excluded by the all-caps macro type and required error.
8473fn recovered_macro_qualified_field_declarators<'tree>(
8474    node: Node<'tree>,
8475    source: &str,
8476) -> Option<Vec<Node<'tree>>> {
8477    if node.kind() != "field_declaration" {
8478        return None;
8479    }
8480    let macro_type = node.child_by_field_name("type")?;
8481    if macro_type.kind() != "type_identifier"
8482        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8483    {
8484        return None;
8485    }
8486    let pseudo_declarator = node.child_by_field_name("declarator")?;
8487    if pseudo_declarator.kind() != "field_identifier" {
8488        return None;
8489    }
8490    let mut cursor = node.walk();
8491    let clause = node
8492        .named_children(&mut cursor)
8493        .find(|child| child.kind() == "bitfield_clause")?;
8494    if !(0..clause.named_child_count()).any(|index| {
8495        clause
8496            .named_child(index)
8497            .is_some_and(|child| child.kind() == "ERROR")
8498    }) {
8499        return None;
8500    }
8501    let mut recovered = Vec::new();
8502    let mut stack = vec![clause];
8503    while let Some(current) = stack.pop() {
8504        if current.kind() == "assignment_expression"
8505            && let Some(left) = current.child_by_field_name("left")
8506            && extract_variable_name(left, source).is_some()
8507        {
8508            recovered.push(left);
8509            break;
8510        }
8511        let mut cursor = current.walk();
8512        stack.extend(current.named_children(&mut cursor));
8513    }
8514    if recovered.is_empty() {
8515        return None;
8516    }
8517    let mut cursor = node.walk();
8518    recovered.extend(
8519        node.children_by_field_name("declarator", &mut cursor)
8520            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
8521    );
8522    Some(recovered)
8523}
8524
8525/// Recover a macro-qualified constructor that tree-sitter represents as one
8526/// field declaration. The constructor call remains inside the direct recovery
8527/// error, while each member initializer becomes a false function declarator.
8528/// The class owner proves the constructor name and lets the caller ignore those
8529/// initializer declarators.
8530fn recovered_macro_qualified_constructor_call<'tree>(
8531    node: Node<'tree>,
8532    class_name: &str,
8533    source: &str,
8534) -> Option<Node<'tree>> {
8535    if node.kind() != "field_declaration" {
8536        return None;
8537    }
8538    let macro_type = node.child_by_field_name("type")?;
8539    if macro_type.kind() != "type_identifier"
8540        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8541    {
8542        return None;
8543    }
8544    let mut cursor = node.walk();
8545    let bitfield = node
8546        .named_children(&mut cursor)
8547        .find(|child| child.kind() == "bitfield_clause")?;
8548    let error = bitfield
8549        .named_child(0)
8550        .filter(|child| child.kind() == "ERROR")?;
8551    let mut stack = vec![error];
8552    while let Some(current) = stack.pop() {
8553        if current.kind() == "call_expression"
8554            && current
8555                .child_by_field_name("function")
8556                .is_some_and(|function| node_text(function, source) == class_name)
8557            && current
8558                .child_by_field_name("arguments")
8559                .is_some_and(|arguments| arguments.kind() == "argument_list")
8560        {
8561            return Some(current);
8562        }
8563        let mut cursor = current.walk();
8564        stack.extend(current.named_children(&mut cursor));
8565    }
8566    None
8567}
8568
8569/// Recover a macro-qualified member function declaration that tree-sitter
8570/// represents as a pseudo-field. An object-like export macro before a qualified
8571/// return type can displace the namespace and type into an ERROR/bitfield
8572/// recovery, leaving the callable as a structured `call_expression`.
8573///
8574/// The caller must route this shape before ordinary declarator classification;
8575/// otherwise the displaced namespace identifier is published as a field.
8576fn recovered_macro_qualified_function_call<'tree>(
8577    node: Node<'tree>,
8578    source: &str,
8579) -> Option<Node<'tree>> {
8580    if node.kind() != "field_declaration" {
8581        return None;
8582    }
8583    let macro_type = node.child_by_field_name("type")?;
8584    if macro_type.kind() != "type_identifier"
8585        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8586    {
8587        return None;
8588    }
8589    let declarator = node.child_by_field_name("declarator")?;
8590    if declarator.kind() != "field_identifier" {
8591        return None;
8592    }
8593    let mut cursor = node.walk();
8594    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8595    if !named.iter().any(|child| {
8596        child.kind() == "storage_class_specifier"
8597            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
8598    }) {
8599        return None;
8600    }
8601    let bitfield = named
8602        .iter()
8603        .find(|child| child.kind() == "bitfield_clause")?;
8604    let mut bitfield_cursor = bitfield.walk();
8605    let payload = bitfield
8606        .named_children(&mut bitfield_cursor)
8607        .collect::<Vec<_>>();
8608    let [displaced_error, call] = payload.as_slice() else {
8609        return None;
8610    };
8611    if displaced_error.kind() != "ERROR"
8612        || displaced_error.named_child_count() != 1
8613        || displaced_error
8614            .named_child(0)
8615            .is_none_or(|child| child.kind() != "identifier")
8616        || call.kind() != "call_expression"
8617        || call
8618            .child_by_field_name("function")
8619            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
8620        || call
8621            .child_by_field_name("arguments")
8622            .is_none_or(|arguments| arguments.kind() != "argument_list")
8623    {
8624        return None;
8625    }
8626    Some(*call)
8627}
8628
8629fn recovered_macro_qualified_function_parameters(
8630    arguments: Node<'_>,
8631    source: &str,
8632) -> Option<(String, Vec<String>)> {
8633    if arguments.kind() != "argument_list" {
8634        return None;
8635    }
8636    let mut cursor = arguments.walk();
8637    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
8638    if named.is_empty() {
8639        return Some(("()".to_string(), Vec::new()));
8640    }
8641    let mut types = Vec::new();
8642    let mut labels = Vec::new();
8643    let mut index = 0;
8644    while index < named.len() {
8645        let parameter_type = named[index];
8646        let parameter_name = named.get(index + 1).copied()?;
8647        if !matches!(
8648            parameter_type.kind(),
8649            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
8650        ) || parameter_name.kind() != "ERROR"
8651            || parameter_name.named_child_count() != 1
8652            || parameter_name
8653                .named_child(0)
8654                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
8655        {
8656            return None;
8657        }
8658        let parameter_name = parameter_name.named_child(0)?;
8659        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
8660        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
8661        index += 2;
8662    }
8663    Some((format!("({})", types.join(", ")), labels))
8664}
8665
8666/// Recognize the phantom field tree-sitter emits for a macro-qualified
8667/// function return type.  For example,
8668/// `static API result_type ThresholdForSmallA() { ... }` can become a
8669/// `field_declaration` (`API` as the type and `result_type` as a field name)
8670/// followed by a clean `function_definition` for `ThresholdForSmallA`.
8671///
8672/// Keep this predicate entirely tied to the CST envelope: the type must be an
8673/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
8674/// the declaration must carry a missing semicolon rather than a real one, and
8675/// the immediate named sibling must expose a function declarator.  A real
8676/// macro-decorated field with an explicit semicolon therefore remains a field.
8677pub fn recovered_macro_return_type_node<'tree>(
8678    node: Node<'tree>,
8679    source: &str,
8680) -> Option<Node<'tree>> {
8681    if node.kind() != "field_declaration" {
8682        return None;
8683    }
8684    let macro_type = node.child_by_field_name("type")?;
8685    if macro_type.kind() != "type_identifier"
8686        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
8687    {
8688        return None;
8689    }
8690    let declarator = node.child_by_field_name("declarator")?;
8691    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
8692        return None;
8693    }
8694    let mut has_missing_semicolon = false;
8695    let mut has_real_semicolon = false;
8696    for index in 0..node.child_count() {
8697        let Some(child) = node.child(index) else {
8698            continue;
8699        };
8700        if child.kind() != ";" {
8701            continue;
8702        }
8703        if child.is_missing() {
8704            has_missing_semicolon = true;
8705        } else {
8706            has_real_semicolon = true;
8707        }
8708    }
8709    if !has_missing_semicolon || has_real_semicolon {
8710        return None;
8711    }
8712    let mut next = node.next_named_sibling();
8713    while next.is_some_and(|sibling| sibling.kind() == "comment") {
8714        next = next.and_then(|sibling| sibling.next_named_sibling());
8715    }
8716    let next = next?;
8717    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
8718        return None;
8719    }
8720    let function_declarator = next.child_by_field_name("declarator")?;
8721    extract_function_declarator(function_declarator).map(|_| declarator)
8722}
8723
8724/// Whether `name` is a type parameter of a template declaration that lexically
8725/// encloses `node`. The malformed macro-return field uses the parameter name as
8726/// its pseudo-declarator; preserving that field is necessary to publish a
8727/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
8728/// ancestors instead of interpreting source text so nested templates and
8729/// parser-recovered regions retain their real lexical scopes.
8730fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
8731    let mut ancestor = node.parent();
8732    while let Some(current) = ancestor {
8733        if current.kind() == "template_declaration"
8734            && let Some(parameters) = current.child_by_field_name("parameters")
8735        {
8736            let mut cursor = parameters.walk();
8737            if parameters.named_children(&mut cursor).any(|parameter| {
8738                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
8739                    && cpp_template_parameter_name(parameter, source)
8740                        .is_some_and(|parameter_name| parameter_name == name)
8741            }) {
8742                return true;
8743            }
8744        }
8745        ancestor = current.parent();
8746    }
8747    false
8748}
8749
8750/// Reparse the region `[start, end)` of `source` as C++, confined to the region
8751/// via included ranges so every reparsed node keeps its original byte offset and
8752/// line number. The existing visitors read node text from the original source,
8753/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
8754/// `parse_rust_region_tree` technique.
8755fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
8756    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
8757}
8758
8759fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
8760    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
8761        return None;
8762    }
8763    let semicolon = node.next_sibling()?;
8764    if semicolon.kind() != ";" || semicolon.is_missing() {
8765        return None;
8766    }
8767    let row = node.start_position().row;
8768    let mut start = node.start_byte();
8769    let mut sibling = node.prev_sibling();
8770    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
8771        if previous.kind() == ";" {
8772            break;
8773        }
8774        start = previous.start_byte();
8775        sibling = previous.prev_sibling();
8776    }
8777    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
8778}
8779
8780fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
8781    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
8782        return false;
8783    }
8784    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
8785        return false;
8786    }
8787    let Some(declarator) = (if node.kind() == "function_definition" {
8788        node.child_by_field_name("declarator")
8789            .and_then(extract_function_declarator)
8790    } else {
8791        node.named_child(0)
8792            .filter(|child| child.kind() == "function_declarator")
8793    }) else {
8794        return false;
8795    };
8796    let Some(name) = cpp_function_declarator_name_node(declarator) else {
8797        return false;
8798    };
8799    declarator.start_byte() == node.start_byte()
8800        && name.kind() == "identifier"
8801        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
8802}
8803
8804/// Reparse a fragmented class-body interior while preserving its original byte
8805/// and line offsets. Unlike an included-range translation-unit parse, a padded
8806/// prefix keeps C++ preprocessor directives after an access label in the same
8807/// recovery shape tree-sitter produces for a complete class body.
8808fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
8809    let bytes = source.as_bytes();
8810    let prefix = bytes.get(..start)?;
8811    let interior = bytes.get(start..end)?;
8812    let mut padded = Vec::with_capacity(end);
8813    padded.extend(
8814        prefix
8815            .iter()
8816            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
8817    );
8818    padded.extend_from_slice(interior);
8819    let padded = String::from_utf8(padded).ok()?;
8820    let mut parser = Parser::new();
8821    parser
8822        .set_language(&tree_sitter_cpp::LANGUAGE.into())
8823        .ok()?;
8824    parser.parse(&padded, None)
8825}
8826
8827/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
8828/// reparsed interior is indexed only when every top-level named node is a
8829/// well-formed C++ item (or a comment) and at least one real item is present.
8830/// Expression/statement soup surfaces as a top-level `ERROR` or
8831/// `expression_statement`, neither of which is an item kind, so it is rejected.
8832///
8833/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
8834/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
8835/// swallowed by a preceding dangling sentinel) reparses to a real
8836/// `namespace_definition` whose body still holds a bogus `function_definition`,
8837/// so the subtree legitimately carries an error. Container items are admitted
8838/// even with an internal error; the inner bogus function is recovered recursively
8839/// when `visit_function_definition` walks it. Each recursion strips at least one
8840/// leading sentinel, so the region strictly shrinks and recovery terminates.
8841///
8842/// A top-level `function_definition` is the one place we stay strict: it is
8843/// admitted only when it is clean or is itself a sentinel candidate. A function
8844/// that has an error and is not a sentinel is a real callable with a broken body,
8845/// so we refuse the whole reparse and let the ordinary path handle it (preserving
8846/// its real return type rather than re-deriving an implicit one).
8847fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
8848    let mut cursor = root.walk();
8849    let mut saw_item = false;
8850    for child in root.named_children(&mut cursor) {
8851        match child.kind() {
8852            "comment" => {}
8853            "function_definition" => {
8854                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
8855                    return false;
8856                }
8857                saw_item = true;
8858            }
8859            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
8860            _ => return false,
8861        }
8862    }
8863    saw_item
8864}
8865
8866/// Robustness gate for a reparsed fragmented multiple-base export class body
8867/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
8868/// kinds a class body produces when reparsed at translation-unit scope: the
8869/// access-specifier label preceding the first member surfaces as a
8870/// `labeled_statement` wrapping that member, and members surface as
8871/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
8872/// Statement or expression soup surfaces as other top-level kinds and is rejected,
8873/// so only a genuinely member-shaped body is ever re-owned as members; anything
8874/// ambiguous falls back to indexing the class alone.
8875fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
8876    if node.kind() != "ERROR" {
8877        return false;
8878    }
8879    let mut stack = Vec::new();
8880    let mut saw_function_declarator = false;
8881    let mut cursor = node.walk();
8882    for child in node.named_children(&mut cursor) {
8883        stack.push(child);
8884    }
8885    while let Some(current) = stack.pop() {
8886        match current.kind() {
8887            // Tree-sitter may wrap adjacent copy-control declarations in a
8888            // nested ERROR. Keep descending only through ERROR wrappers; the
8889            // actual declaration payload must be a function_declarator.
8890            "ERROR" => {
8891                let mut cursor = current.walk();
8892                stack.extend(current.named_children(&mut cursor));
8893            }
8894            "function_declarator" => saw_function_declarator = true,
8895            _ => return false,
8896        }
8897    }
8898    saw_function_declarator
8899}
8900
8901fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
8902    if node.kind() != "ERROR" {
8903        return false;
8904    }
8905    let mut cursor = node.walk();
8906    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
8907    let [explicit, constructor_error, destructor] = named.as_slice() else {
8908        return false;
8909    };
8910    let Some(constructor) = constructor_error.named_child(0) else {
8911        return false;
8912    };
8913    let Some(constructor_name) =
8914        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
8915    else {
8916        return false;
8917    };
8918    let Some(destructor_name) =
8919        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
8920    else {
8921        return false;
8922    };
8923    let Some(destroyed_type) = destructor_name.named_child(0) else {
8924        return false;
8925    };
8926    explicit.kind() == "explicit_function_specifier"
8927        && constructor_error.kind() == "ERROR"
8928        && constructor_error.named_child_count() == 1
8929        && constructor.kind() == "function_declarator"
8930        && constructor_name.kind() == "identifier"
8931        && destructor.kind() == "function_declarator"
8932        && destructor_name.kind() == "destructor_name"
8933        && destroyed_type.kind() == "identifier"
8934        && node_text(constructor_name, source) == node_text(destroyed_type, source)
8935}
8936
8937fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
8938    if node.kind() != "compound_statement" {
8939        return false;
8940    }
8941    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
8942        return false;
8943    };
8944    if prefix.kind() == "labeled_statement"
8945        && prefix.named_child(0).is_some_and(|label| {
8946            matches!(
8947                node_text(label, source).trim(),
8948                "public" | "private" | "protected"
8949            )
8950        })
8951    {
8952        return prefix.named_children(&mut prefix.walk()).any(|child| {
8953            child.kind() == "declaration"
8954                && child.has_error()
8955                && child
8956                    .named_children(&mut child.walk())
8957                    .any(cpp_reparsed_member_error_is_indexable)
8958        });
8959    }
8960    // A malformed constructor initializer can be split into a declaration
8961    // followed by its compound body when the class prefix already contains
8962    // realistic members. Keep this admission tied to that exact structured
8963    // declaration/error/body chain rather than accepting arbitrary blocks.
8964    prefix.kind() == "declaration"
8965        && prefix.has_error()
8966        && prefix
8967            .named_children(&mut prefix.walk())
8968            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
8969}
8970
8971fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
8972    if !cpp_reparsed_member_error_is_indexable(node) {
8973        return false;
8974    }
8975    let Some(preproc) = node.next_named_sibling() else {
8976        return false;
8977    };
8978    preproc.kind() == "preproc_if"
8979        && preproc.has_error()
8980        && preproc
8981            .named_children(&mut preproc.walk())
8982            .any(|child| child.kind() == "expression_statement" && child.has_error())
8983        && preproc
8984            .next_named_sibling()
8985            .is_some_and(|body| body.kind() == "compound_statement")
8986}
8987
8988/// Return a function body whose braces and ownership are explicit in the
8989/// reparsed class-member tree. An error below a real function envelope is
8990/// recoverable by the ordinary function visitor; a missing/deferred body is
8991/// not, because accepting it would let statement soup masquerade as a member.
8992fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
8993    if node.kind() != "function_definition" {
8994        return None;
8995    }
8996    let body = node.child_by_field_name("body")?;
8997    if body.kind() != "compound_statement" {
8998        return None;
8999    }
9000    let open = body.child(0)?;
9001    let close = body.child(body.child_count().checked_sub(1)?)?;
9002    if open.kind() != "{"
9003        || open.is_missing()
9004        || close.kind() != "}"
9005        || close.is_missing()
9006        || close.end_byte() != body.end_byte()
9007        || body.end_byte() != node.end_byte()
9008    {
9009        return None;
9010    }
9011    Some(body)
9012}
9013
9014fn cpp_reparsed_member_function_errors_are_in_body(
9015    node: Node<'_>,
9016    body: Node<'_>,
9017    source: &str,
9018) -> bool {
9019    let mut cursor = node.walk();
9020    node.children(&mut cursor).all(|child| {
9021        same_node(child, body)
9022            || cpp_reparsed_member_attribute_error(child, source)
9023            || cpp_reparsed_member_signature_identifier_errors(child)
9024            || (!child.has_error() && !child.is_error() && !child.is_missing())
9025    })
9026}
9027
9028/// A complete callable can still carry parser errors in its signature when a
9029/// project annotation is not part of the C++ grammar (`nonneg int`,
9030/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
9031/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
9032/// leaves inside the already-proven callable envelope; structured statements,
9033/// literals, missing tokens, and other malformed signature payload remain
9034/// rejected.
9035fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
9036    if !node.has_error() && !node.is_error() && !node.is_missing() {
9037        return false;
9038    }
9039    let mut stack = vec![node];
9040    let mut saw_error = false;
9041    while let Some(current) = stack.pop() {
9042        if current.is_missing() {
9043            return false;
9044        }
9045        if current.kind() == "ERROR" {
9046            saw_error = true;
9047            let mut cursor = current.walk();
9048            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9049            if children
9050                .iter()
9051                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
9052            {
9053                return false;
9054            }
9055            stack.extend(children);
9056            continue;
9057        }
9058        let mut cursor = current.walk();
9059        stack.extend(current.children(&mut cursor));
9060    }
9061    saw_error
9062}
9063
9064fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
9065    node.kind() == "ERROR"
9066        && node.named_child_count() == 1
9067        && node.named_child(0).is_some_and(|attribute| {
9068            attribute.kind() == "identifier"
9069                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
9070        })
9071}
9072
9073/// A C++ attribute placed between a member's declarator and body can make
9074/// tree-sitter expose the callable as
9075/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
9076/// Keep this admission tied to that exact node geometry. In particular, an
9077/// arbitrary ERROR or identifier before a compound statement is not enough.
9078fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
9079    let Some(body) = cpp_reparsed_member_function_body(node) else {
9080        return false;
9081    };
9082    let mut cursor = node.walk();
9083    let named = node
9084        .named_children(&mut cursor)
9085        .filter(|child| child.kind() != "comment")
9086        .collect::<Vec<_>>();
9087    let [type_node, error, attribute, body_node] = named.as_slice() else {
9088        return false;
9089    };
9090    if !same_node(*body_node, body)
9091        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9092        || attribute.kind() != "identifier"
9093        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9094        || error.kind() != "ERROR"
9095        || error.named_child_count() != 1
9096    {
9097        return false;
9098    }
9099    error
9100        .named_child(0)
9101        .is_some_and(cpp_reparsed_attribute_callable_declarator)
9102}
9103
9104fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
9105    cpp_structured_type_path(node, source).is_some()
9106        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
9107}
9108
9109fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9110    let Some(body) = cpp_reparsed_member_function_body(node) else {
9111        return false;
9112    };
9113    let mut cursor = node.walk();
9114    let named = node
9115        .named_children(&mut cursor)
9116        .filter(|child| child.kind() != "comment")
9117        .collect::<Vec<_>>();
9118    let [friend, return_error, declarator, body_node] = named.as_slice() else {
9119        return false;
9120    };
9121    let Some(return_type) = return_error.named_child(0) else {
9122        return false;
9123    };
9124    same_node(*body_node, body)
9125        && friend.kind() == "type_identifier"
9126        && node_text(*friend, source) == "friend"
9127        && return_error.kind() == "ERROR"
9128        && return_error.named_child_count() == 1
9129        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9130        && extract_function_declarator(*declarator)
9131            .and_then(cpp_function_declarator_name_node)
9132            .is_some()
9133}
9134
9135fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9136    let Some(body) = cpp_reparsed_member_function_body(node) else {
9137        return false;
9138    };
9139    let mut cursor = node.walk();
9140    let named = node
9141        .named_children(&mut cursor)
9142        .filter(|child| child.kind() != "comment")
9143        .collect::<Vec<_>>();
9144    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
9145        return false;
9146    };
9147    let Some(return_type) = return_error.named_child(0) else {
9148        return false;
9149    };
9150    same_node(*body_node, body)
9151        && prefix
9152            .iter()
9153            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
9154        && attribute.kind() == "type_identifier"
9155        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9156        && return_error.kind() == "ERROR"
9157        && return_error.named_child_count() == 1
9158        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9159        && extract_function_declarator(*declarator)
9160            .and_then(cpp_function_declarator_name_node)
9161            .is_some()
9162}
9163
9164/// An included-range reparse that begins inside a malformed class can merge an
9165/// access label and following template member. Tree-sitter then emits the label
9166/// as the `template_type` name, the template parameter list as its arguments,
9167/// an ERROR-wrapped return type, the callable declarator, and its complete
9168/// body. Admit only that exact structured displacement.
9169fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9170    let Some(body) = cpp_reparsed_member_function_body(node) else {
9171        return false;
9172    };
9173    let mut cursor = node.walk();
9174    let named = node
9175        .named_children(&mut cursor)
9176        .filter(|child| child.kind() != "comment")
9177        .collect::<Vec<_>>();
9178    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
9179        return false;
9180    };
9181    let Some(template_name) = template_type.child_by_field_name("name") else {
9182        return false;
9183    };
9184    let Some(arguments) = template_type.child_by_field_name("arguments") else {
9185        return false;
9186    };
9187    let Some(return_type) = return_error.named_child(0) else {
9188        return false;
9189    };
9190    let mut cursor = template_type.walk();
9191    let template_errors = template_type
9192        .named_children(&mut cursor)
9193        .filter(|child| child.kind() == "ERROR")
9194        .collect::<Vec<_>>();
9195    let [comment_error] = template_errors.as_slice() else {
9196        return false;
9197    };
9198    let mut cursor = comment_error.walk();
9199    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
9200    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
9201        return false;
9202    };
9203    same_node(*body_node, body)
9204        && template_type.kind() == "template_type"
9205        && template_name.kind() == "type_identifier"
9206        && matches!(
9207            node_text(template_name, source).trim(),
9208            "public" | "private" | "protected"
9209        )
9210        && arguments.kind() == "template_argument_list"
9211        && arguments.named_child_count() > 0
9212        && !arguments.has_error()
9213        && !colon.is_named()
9214        && colon.kind() == ":"
9215        && comments.iter().all(|child| child.kind() == "comment")
9216        && !template_keyword.is_named()
9217        && template_keyword.kind() == "template"
9218        && return_error.kind() == "ERROR"
9219        && return_error.named_child_count() == 1
9220        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
9221        && extract_function_declarator(*declarator)
9222            .and_then(cpp_function_declarator_name_node)
9223            .is_some()
9224}
9225
9226/// Return the constructor declaration tree-sitter can merge into an access
9227/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
9228/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
9229/// declaration's apparent type; the callable name must still exactly match the
9230/// recovered class, so unrelated labeled statements are never re-owned.
9231fn cpp_reparsed_preprocessor_constructor<'tree>(
9232    node: Node<'tree>,
9233    class_name: &str,
9234    source: &str,
9235) -> Option<Node<'tree>> {
9236    if node.kind() != "labeled_statement" {
9237        return None;
9238    }
9239    let mut cursor = node.walk();
9240    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9241    let [label, directive_error, declaration] = named.as_slice() else {
9242        return None;
9243    };
9244    if label.kind() != "statement_identifier"
9245        || !matches!(
9246            node_text(*label, source),
9247            "public" | "private" | "protected"
9248        )
9249        || directive_error.kind() != "ERROR"
9250        || directive_error.child_count() != 1
9251        || directive_error
9252            .child(0)
9253            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
9254        || declaration.kind() != "declaration"
9255        || declaration.named_child_count() != 2
9256    {
9257        return None;
9258    }
9259    let apparent_type = declaration.child_by_field_name("type")?;
9260    if apparent_type.kind() != "type_identifier"
9261        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
9262    {
9263        return None;
9264    }
9265    let declarator = declaration.child_by_field_name("declarator")?;
9266    let function = extract_function_declarator(declarator)?;
9267    let name = cpp_function_declarator_name_node(function)?;
9268    (node_text(name, source) == class_name).then_some(*declaration)
9269}
9270
9271fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
9272    if extract_function_declarator(node)
9273        .and_then(cpp_function_declarator_name_node)
9274        .is_some()
9275    {
9276        return true;
9277    }
9278    node.kind() == "init_declarator"
9279        && node
9280            .child_by_field_name("declarator")
9281            .is_some_and(|declarator| declarator.kind() == "identifier")
9282        && node
9283            .child_by_field_name("value")
9284            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
9285}
9286
9287/// Return true for the constrained/attribute form that tree-sitter splits into
9288/// an ERROR declaration, a preprocessor `requires` clause, and a following
9289/// compound statement. The three nodes must remain immediate named siblings;
9290/// this deliberately does not search source text or skip unrelated statements.
9291fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
9292    if node.kind() != "ERROR" || node.named_child_count() != 3 {
9293        return false;
9294    }
9295    let mut cursor = node.walk();
9296    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9297    let [type_node, function_declarator, attribute] = named.as_slice() else {
9298        return false;
9299    };
9300    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
9301        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
9302        || attribute.kind() != "identifier"
9303        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
9304    {
9305        return false;
9306    }
9307    let Some(preproc) =
9308        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9309    else {
9310        return false;
9311    };
9312    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
9313        .filter(|sibling| sibling.kind() == "compound_statement")
9314    else {
9315        return false;
9316    };
9317    let Some(open) = body.child(0) else {
9318        return false;
9319    };
9320    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
9321        return false;
9322    };
9323    let Some(condition) = preproc.child_by_field_name("condition") else {
9324        return false;
9325    };
9326    let mut cursor = preproc.walk();
9327    let payload = preproc
9328        .named_children(&mut cursor)
9329        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
9330        .collect::<Vec<_>>();
9331    let [requires_statement] = payload.as_slice() else {
9332        return false;
9333    };
9334    let requires_clause = requires_statement.named_child(0);
9335
9336    open.kind() == "{"
9337        && !open.is_missing()
9338        && close.kind() == "}"
9339        && !close.is_missing()
9340        && close.end_byte() == body.end_byte()
9341        && requires_statement.kind() == "expression_statement"
9342        && requires_statement.named_child_count() == 1
9343        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
9344}
9345
9346fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9347    let mut sibling = node.next_named_sibling();
9348    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9349        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
9350    }
9351    sibling
9352}
9353
9354fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
9355    let mut sibling = node.prev_named_sibling();
9356    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
9357        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
9358    }
9359    sibling
9360}
9361
9362fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
9363    let Some(preproc) =
9364        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
9365    else {
9366        return false;
9367    };
9368    let Some(error) =
9369        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
9370    else {
9371        return false;
9372    };
9373    cpp_reparsed_attribute_requires_error(error, source)
9374}
9375
9376fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
9377    node: Node<'tree>,
9378    source: &str,
9379) -> Option<Node<'tree>> {
9380    if node.kind() != "ERROR" {
9381        return None;
9382    }
9383    let mut cursor = node.walk();
9384    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
9385    let [parameter, macro_name, message] = named.as_slice() else {
9386        return None;
9387    };
9388    let parameter_name = parameter.named_child(0)?;
9389    (parameter.kind() == "type_parameter_declaration"
9390        && parameter_name.kind() == "type_identifier"
9391        && macro_name.kind() == "type_identifier"
9392        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
9393        && message.kind() == "string_literal")
9394        .then_some(parameter_name)
9395}
9396
9397fn cpp_reparsed_template_macro_companion_is_indexable(
9398    node: Node<'_>,
9399    parameter_name: Node<'_>,
9400    source: &str,
9401) -> bool {
9402    let Some(body) = cpp_reparsed_member_function_body(node) else {
9403        return false;
9404    };
9405    let mut cursor = node.walk();
9406    let named = node
9407        .named_children(&mut cursor)
9408        .filter(|child| child.kind() != "comment")
9409        .collect::<Vec<_>>();
9410    let [
9411        constraint,
9412        close_error,
9413        storage,
9414        return_error,
9415        declarator,
9416        body_node,
9417    ] = named.as_slice()
9418    else {
9419        return false;
9420    };
9421    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
9422        return false;
9423    };
9424    let Some(constraint_template) = constraint.child_by_field_name("name") else {
9425        return false;
9426    };
9427    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
9428        return false;
9429    };
9430    let Some(return_type) = return_error.named_child(0) else {
9431        return false;
9432    };
9433    let mut cursor = constraint_arguments.walk();
9434    let constraint_types = constraint_arguments
9435        .named_children(&mut cursor)
9436        .collect::<Vec<_>>();
9437    same_node(*body_node, body)
9438        && constraint.kind() == "qualified_identifier"
9439        && constraint_scope.kind() == "namespace_identifier"
9440        && constraint_template.kind() == "template_type"
9441        && matches!(constraint_types.as_slice(), [left, right]
9442            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9443        && !constraint_arguments.has_error()
9444        && close_error.kind() == "ERROR"
9445        && close_error.named_child_count() == 0
9446        && storage.kind() == "storage_class_specifier"
9447        && return_error.kind() == "ERROR"
9448        && return_error.named_child_count() == 1
9449        && return_type.kind() == "identifier"
9450        && node_text(return_type, source) == node_text(parameter_name, source)
9451        && extract_function_declarator(*declarator)
9452            .and_then(cpp_function_declarator_name_node)
9453            .is_some()
9454}
9455
9456fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
9457    node: Node<'tree>,
9458    parameter_name: Node<'_>,
9459    source: &str,
9460) -> Option<Node<'tree>> {
9461    let body = cpp_reparsed_member_function_body(node)?;
9462    let constraint = node.child_by_field_name("type")?;
9463    let constraint_template = constraint.child_by_field_name("name")?;
9464    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
9465    let mut argument_cursor = constraint_arguments.walk();
9466    let constraint_types = constraint_arguments
9467        .named_children(&mut argument_cursor)
9468        .collect::<Vec<_>>();
9469    if constraint.kind() != "qualified_identifier"
9470        || constraint_template.kind() != "template_type"
9471        || !matches!(constraint_types.as_slice(), [left, right]
9472            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
9473        || constraint_arguments.has_error()
9474        || node
9475            .child_by_field_name("body")
9476            .is_none_or(|candidate| !same_node(candidate, body))
9477    {
9478        return None;
9479    }
9480
9481    let mut cursor = node.walk();
9482    let recovery_errors = node
9483        .named_children(&mut cursor)
9484        .filter(|child| child.kind() == "ERROR")
9485        .collect::<Vec<_>>();
9486    if !recovery_errors
9487        .iter()
9488        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
9489        || !recovery_errors.iter().all(|error| {
9490            error.named_child_count() == 0
9491                || cpp_reparsed_constraint_macro_error(*error, source)
9492                || (error.named_child_count() == 1
9493                    && error
9494                        .named_child(0)
9495                        .is_some_and(|child| child.kind() == "function_declarator"))
9496        })
9497    {
9498        return None;
9499    }
9500
9501    let parameter_text = node_text(parameter_name, source);
9502    let mut declarators = node
9503        .child_by_field_name("declarator")
9504        .and_then(extract_function_declarator)
9505        .into_iter()
9506        .collect::<Vec<_>>();
9507    for error in recovery_errors {
9508        let mut stack = vec![error];
9509        while let Some(current) = stack.pop() {
9510            if current.kind() == "function_declarator" {
9511                declarators.push(current);
9512            }
9513            let mut cursor = current.walk();
9514            stack.extend(current.named_children(&mut cursor));
9515        }
9516    }
9517    declarators.into_iter().find(|declarator| {
9518        cpp_function_declarator_name_node(*declarator)
9519            .is_some_and(|name| name.kind() == "identifier")
9520            && declarator
9521                .child_by_field_name("parameters")
9522                .is_some_and(|parameters| {
9523                    parameters
9524                        .named_children(&mut parameters.walk())
9525                        .filter_map(|parameter| parameter.child_by_field_name("type"))
9526                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
9527                })
9528    })
9529}
9530
9531fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
9532    node: Node<'_>,
9533    parameter_name: Node<'_>,
9534    source: &str,
9535) -> bool {
9536    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
9537}
9538
9539fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
9540    if node.kind() != "ERROR" {
9541        return false;
9542    }
9543    let mut stack = vec![node];
9544    while let Some(current) = stack.pop() {
9545        let macro_shape = match current.kind() {
9546            "call_expression" => current
9547                .child_by_field_name("function")
9548                .zip(current.child_by_field_name("arguments")),
9549            "init_declarator" => current
9550                .child_by_field_name("declarator")
9551                .zip(current.child_by_field_name("value")),
9552            _ => None,
9553        };
9554        if let Some((name, arguments)) = macro_shape
9555            && name.kind() == "identifier"
9556            && arguments.kind() == "argument_list"
9557            && arguments.named_child_count() >= 2
9558            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
9559        {
9560            return true;
9561        }
9562        let mut cursor = current.walk();
9563        stack.extend(current.named_children(&mut cursor));
9564    }
9565    false
9566}
9567
9568fn cpp_recovered_template_macro_constructor<'tree>(
9569    node: Node<'tree>,
9570    source: &str,
9571) -> Option<(Node<'tree>, Node<'tree>)> {
9572    let mut prefix = node.prev_named_sibling()?;
9573    while prefix.kind() == "comment" {
9574        prefix = prefix.prev_named_sibling()?;
9575    }
9576    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
9577    let parameter = parameter_name
9578        .parent()
9579        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
9580    let declarator =
9581        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
9582    Some((declarator, parameter))
9583}
9584
9585fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
9586    let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) else {
9587        return false;
9588    };
9589    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
9590        cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
9591            || cpp_reparsed_template_macro_constructor_companion_is_indexable(
9592                function,
9593                parameter_name,
9594                source,
9595            )
9596    })
9597}
9598
9599fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
9600    let function_name = node
9601        .child_by_field_name("declarator")
9602        .and_then(extract_function_declarator)
9603        .and_then(cpp_function_declarator_name_node);
9604    if let Some(body) = cpp_reparsed_member_function_body(node)
9605        && function_name.is_some()
9606        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
9607    {
9608        return true;
9609    }
9610    cpp_reparsed_attribute_member_function(node, source)
9611        || cpp_reparsed_friend_function_is_indexable(node, source)
9612        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
9613        || cpp_reparsed_access_template_function_is_indexable(node, source)
9614        || cpp_recovered_template_macro_constructor(node, source).is_some()
9615}
9616
9617fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
9618    let mut cursor = root.walk();
9619    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
9620    let mut saw_member = false;
9621    let mut index = 0;
9622    while index < children.len() {
9623        let child = children[index];
9624        if let Some((_, fragmented)) = fragmented_plain_class_body(child, source) {
9625            let Some(tree) = cpp_reparse_fragmented_class_body(
9626                source,
9627                fragmented.reparse_start,
9628                fragmented.reparse_end,
9629            ) else {
9630                return false;
9631            };
9632            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9633                return false;
9634            }
9635            saw_member = true;
9636            index += 1;
9637            while index < children.len()
9638                && children[index].end_byte() <= fragmented.class_range.end_byte
9639            {
9640                index += 1;
9641            }
9642            continue;
9643        }
9644        match child.kind() {
9645            "comment" => {}
9646            "labeled_statement" => saw_member = true,
9647            "function_definition" => {
9648                if child.has_error()
9649                    && !cpp_reparsed_member_function_is_indexable(child, source)
9650                    && cpp_sentinel_macro_region(child, source).is_none()
9651                {
9652                    return false;
9653                }
9654                saw_member = true;
9655            }
9656            "ERROR"
9657                if (cpp_reparsed_member_error_is_indexable(child)
9658                    || cpp_reparsed_adjacent_copy_control_error(child, source))
9659                    && (child
9660                        .next_named_sibling()
9661                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
9662                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
9663            {
9664                saw_member = true;
9665            }
9666            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
9667                saw_member = true;
9668            }
9669            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
9670                saw_member = true;
9671            }
9672            "expression_statement"
9673                if cpp_is_stray_semicolon(child, source)
9674                    && child.prev_named_sibling().is_some_and(|error| {
9675                        cpp_reparsed_member_error_is_indexable(error)
9676                            || cpp_reparsed_adjacent_copy_control_error(error, source)
9677                    }) =>
9678            {
9679                saw_member = true;
9680            }
9681            "compound_statement"
9682                if cpp_reparsed_constructor_body_is_indexable(child, source)
9683                    || cpp_reparsed_attribute_requires_body(child, source) =>
9684            {
9685                saw_member = true;
9686            }
9687            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
9688            _ => return false,
9689        }
9690        index += 1;
9691    }
9692    saw_member
9693}
9694
9695/// Detect the malformed constructor shape that tree-sitter exposes as an
9696/// access-label statement followed by initializer-looking declarations. The
9697/// declarations are not class members: visiting their `location(loc)` and
9698/// `string(s)` function declarators would publish synthetic functions. The
9699/// export-class fallback keeps the original sibling nodes and therefore avoids
9700/// this parser artifact. The returned range identifies the real constructor
9701/// header, which can be reparsed independently as a structured declarator.
9702fn cpp_reparsed_synthetic_initializer_constructor_range(
9703    root: Node<'_>,
9704    class_name: &str,
9705    source: &str,
9706    constructor_end: usize,
9707) -> Option<std::ops::Range<usize>> {
9708    let mut stack = {
9709        let mut cursor = root.walk();
9710        root.named_children(&mut cursor).collect::<Vec<_>>()
9711    };
9712    while let Some(current) = stack.pop() {
9713        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
9714            current,
9715            class_name,
9716            source,
9717            constructor_end,
9718        ) {
9719            return Some(range);
9720        }
9721        if current.kind() == "ERROR" {
9722            let mut cursor = current.walk();
9723            stack.extend(current.named_children(&mut cursor));
9724        }
9725    }
9726    None
9727}
9728
9729fn cpp_reparsed_synthetic_initializer_constructor(
9730    node: Node<'_>,
9731    class_name: &str,
9732    source: &str,
9733    constructor_end: usize,
9734) -> Option<std::ops::Range<usize>> {
9735    if node.kind() != "labeled_statement" {
9736        return None;
9737    }
9738    let mut cursor = node.walk();
9739    let named = node
9740        .named_children(&mut cursor)
9741        .filter(|child| child.kind() != "comment")
9742        .collect::<Vec<_>>();
9743    let label = named.first()?;
9744    if label.kind() != "statement_identifier"
9745        || !matches!(
9746            node_text(*label, source).trim(),
9747            "public" | "private" | "protected"
9748        )
9749    {
9750        return None;
9751    }
9752    let call_error_index = named.iter().position(|child| {
9753        if child.kind() != "ERROR" {
9754            return false;
9755        }
9756        let mut stack = vec![*child];
9757        while let Some(current) = stack.pop() {
9758            if current.kind() == "call_expression"
9759                && current
9760                    .child_by_field_name("function")
9761                    .is_some_and(|function| {
9762                        function.kind() == "identifier"
9763                            && node_text(function, source).trim() == class_name
9764                    })
9765            {
9766                return true;
9767            }
9768            let mut cursor = current.walk();
9769            stack.extend(current.named_children(&mut cursor));
9770        }
9771        false
9772    })?;
9773    let constructor_call = {
9774        let mut stack = vec![named[call_error_index]];
9775        let mut found = None;
9776        while let Some(current) = stack.pop() {
9777            if current.kind() == "call_expression"
9778                && current
9779                    .child_by_field_name("function")
9780                    .is_some_and(|function| {
9781                        function.kind() == "identifier"
9782                            && node_text(function, source).trim() == class_name
9783                    })
9784            {
9785                found = Some(current);
9786                break;
9787            }
9788            let mut cursor = current.walk();
9789            stack.extend(current.named_children(&mut cursor));
9790        }
9791        found
9792    };
9793    let constructor_call = constructor_call?;
9794    named.iter().skip(call_error_index + 1).find(|child| {
9795        child.kind() == "declaration" && child.has_error() && {
9796            let mut cursor = child.walk();
9797            child.named_children(&mut cursor).any(|declarator| {
9798                declarator.kind() == "init_declarator"
9799                    && declarator
9800                        .child_by_field_name("declarator")
9801                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
9802                    && declarator
9803                        .child_by_field_name("value")
9804                        .is_some_and(|value| value.kind() == "initializer_list")
9805            })
9806        }
9807    })?;
9808    Some(constructor_call.start_byte()..constructor_end)
9809}
9810
9811fn cpp_reparsed_exact_constructor_declarator<'tree>(
9812    root: Node<'tree>,
9813    start: usize,
9814    class_name: &str,
9815    source: &str,
9816) -> Option<Node<'tree>> {
9817    let mut candidate = None;
9818    let mut stack = vec![root];
9819    while let Some(current) = stack.pop() {
9820        if current.kind() == "function_declarator"
9821            && current.start_byte() == start
9822            && cpp_function_declarator_name_node(current)
9823                .is_some_and(|name| node_text(name, source).trim() == class_name)
9824        {
9825            if candidate.is_some() {
9826                return None;
9827            }
9828            candidate = Some(current);
9829            continue;
9830        }
9831        let mut cursor = current.walk();
9832        stack.extend(current.named_children(&mut cursor));
9833    }
9834    candidate
9835}
9836
9837fn cpp_is_indexable_item_kind(kind: &str) -> bool {
9838    matches!(
9839        kind,
9840        "namespace_definition"
9841            | "class_specifier"
9842            | "struct_specifier"
9843            | "union_specifier"
9844            | "enum_specifier"
9845            | "function_definition"
9846            | "template_declaration"
9847            | "declaration"
9848            | "field_declaration"
9849            | "alias_declaration"
9850            | "static_assert_declaration"
9851            | "type_definition"
9852            | "using_declaration"
9853            | "linkage_specification"
9854            | "preproc_def"
9855            | "preproc_function_def"
9856            | "preproc_include"
9857            | "preproc_if"
9858            | "preproc_ifdef"
9859            | "preproc_call"
9860    )
9861}
9862
9863#[cfg(test)]
9864mod tests {
9865    use super::*;
9866    use crate::adapter::parse_cpp_file;
9867    use brokk_bifrost_core::analyzer::parsed_file::{
9868        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
9869    };
9870    use std::fmt::Write;
9871
9872    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
9873        let mut parser = tree_sitter::Parser::new();
9874        parser
9875            .set_language(&tree_sitter_cpp::LANGUAGE.into())
9876            .unwrap();
9877        let tree = parser.parse(source, None).unwrap();
9878        let file = ProjectFile::new(std::env::temp_dir(), name);
9879        parse_cpp_file(&file, source, &tree)
9880    }
9881
9882    #[test]
9883    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
9884        let source = r#"namespace control {
9885template <typename T>
9886class AnySpan;
9887template <typename T>
9888class ABSL_ATTRIBUTE_VIEW AnySpan {
9889 public:
9890  int begin() const;
9891};
9892}
9893
9894namespace absl {
9895ABSL_NAMESPACE_BEGIN
9896template <typename T>
9897class ABSL_ATTRIBUTE_VIEW Span {
9898 public:
9899  int begin() const;
9900  int back() const;
9901};
9902
9903int begin();
9904int back();
9905}
9906"#;
9907        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
9908        let declarations = parsed.declarations();
9909        assert!(
9910            declarations
9911                .iter()
9912                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
9913        );
9914        for method in ["begin", "back"] {
9915            assert!(declarations.iter().any(|unit| {
9916                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
9917            }));
9918            assert!(
9919                declarations.iter().any(|unit| {
9920                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
9921                })
9922            );
9923        }
9924        assert!(
9925            declarations
9926                .iter()
9927                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
9928        );
9929        assert!(
9930            declarations
9931                .iter()
9932                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
9933        );
9934        assert!(
9935            declarations
9936                .iter()
9937                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
9938        );
9939    }
9940
9941    #[test]
9942    fn explicit_global_member_definition_has_canonical_package_boundary() {
9943        let source = r#"
9944namespace arangodb::aql {
9945class ExecutionPlan {
9946 public:
9947  template<class... Args> Node* createNode(Args&&... args);
9948};
9949}
9950
9951template<class... Args>
9952Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
9953"#;
9954        let parsed = parse_cpp_declarations(source, "global-member.cpp");
9955
9956        assert!(parsed.declarations().iter().any(|unit| {
9957            unit.is_function()
9958                && unit.package_name() == "arangodb::aql"
9959                && unit.short_name() == "ExecutionPlan.createNode"
9960                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
9961        }));
9962    }
9963
9964    #[test]
9965    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
9966        let source = r#"
9967#ifndef TINYXML2_INCLUDED
9968#define TINYXML2_INCLUDED
9969namespace tinyxml2 {
9970class TINYXML2_LIB XMLUtil {
9971 public:
9972  static const char* SkipWhiteSpace(const char* p) {
9973    while (*p) {
9974      if (*p == ' ') {
9975        ++p;
9976      }
9977    }
9978    return p;
9979  }
9980  static bool StringEqual(const char* p, const char* q) {
9981    return p == q;
9982  }
9983  class TINYXML2_LIB Helper {
9984   public:
9985    void Touch();
9986  };
9987  static void ToStr(int value, char* buffer);
9988 private:
9989  static const char* writeBoolTrue;
9990};
9991
9992class TINYXML2_LIB XMLNode {
9993 public:
9994  virtual XMLNode* ShallowClone() const = 0;
9995  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
9996};
9997}
9998#endif
9999"#;
10000        let mut parser = tree_sitter::Parser::new();
10001        parser
10002            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10003            .unwrap();
10004        let tree = parser.parse(source, None).unwrap();
10005        let mut boundary_found = false;
10006        walk_named_tree_preorder(tree.root_node(), true, |node| {
10007            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
10008                && name == "XMLUtil"
10009            {
10010                boundary_found = fragmented_export_sibling_class_boundary(node, source)
10011                    .and_then(|boundary| {
10012                        recover_exported_class_function_definition(boundary, source)
10013                    })
10014                    .is_some_and(|(_, name, _)| name == "XMLNode");
10015            }
10016            WalkControl::Continue
10017        });
10018        assert!(
10019            boundary_found,
10020            "fixture must exercise the recovered sibling boundary"
10021        );
10022
10023        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
10024        assert!(
10025            parsed
10026                .declarations()
10027                .iter()
10028                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
10029            "{:#?}",
10030            parsed.declarations()
10031        );
10032        assert!(
10033            parsed
10034                .declarations()
10035                .iter()
10036                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
10037            "{:#?}",
10038            parsed.declarations()
10039        );
10040        assert!(parsed.declarations().iter().any(|unit| {
10041            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
10042        }));
10043        assert!(
10044            parsed
10045                .declarations()
10046                .iter()
10047                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
10048        );
10049        assert!(
10050            parsed
10051                .declarations()
10052                .iter()
10053                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
10054        );
10055    }
10056
10057    #[test]
10058    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
10059        // Clang's diagnostic suite intentionally contains this ill-formed
10060        // spelling. The analyzer must retain the parser's explicit-global AST
10061        // boundary instead of constructing `cwg311::::cwg311::X`.
10062        let parsed = parse_cpp_declarations(
10063            r#"
10064namespace cwg311 {
10065namespace X { namespace Y {} }
10066namespace ::cwg311::X {}
10067}
10068"#,
10069            "explicit-global-namespace.cpp",
10070        );
10071
10072        assert!(parsed.declarations().iter().any(|unit| {
10073            unit.kind() == CodeUnitType::Module
10074                && unit.short_name() == "cwg311::X"
10075                && unit.fq_name() == "cwg311::X"
10076        }));
10077        assert!(
10078            parsed
10079                .declarations()
10080                .iter()
10081                .all(|unit| !unit.short_name().contains("::::")),
10082            "recovered namespace names must not retain empty scope components: {:#?}",
10083            parsed.declarations()
10084        );
10085    }
10086
10087    #[test]
10088    fn repeated_scope_separator_does_not_create_empty_function_owner() {
10089        let scope = ScopeInfo {
10090            package_name: "X".to_string(),
10091            module: None,
10092            class_unit: None,
10093            template_signature: None,
10094            template_metadata: None,
10095            declarations_are_fields: false,
10096            recovered_specialization_member_scope: false,
10097            visible_using_namespaces: Vec::new(),
10098        };
10099
10100        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
10101
10102        assert_eq!(owner, None);
10103        assert_eq!(name, "doit");
10104        assert_eq!(package, "X");
10105    }
10106
10107    #[test]
10108    fn trailing_decltype_expression_is_not_a_function_declarator() {
10109        let source = r#"
10110namespace boost { namespace detail {
10111#if ! defined(BOOST_NO_SFINAE_EXPR) && \
10112    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
10113    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
10114#define BOOST_THREAD_PROVIDES_INVOKE
10115#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
10116template <class Fp, class A0, class ...Args>
10117inline auto
10118invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
10119       BOOST_THREAD_RV_REF(Args) ...args)
10120    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
10121{
10122    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
10123}
10124#endif
10125#endif
10126}}
10127"#;
10128        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
10129
10130        assert!(
10131            parsed
10132                .declarations()
10133                .iter()
10134                .all(|unit| unit.short_name() != ".*f")
10135        );
10136    }
10137
10138    fn find_class_named<'tree>(
10139        root: Node<'tree>,
10140        source: &str,
10141        expected_name: &str,
10142    ) -> Option<Node<'tree>> {
10143        let mut stack = vec![root];
10144        while let Some(node) = stack.pop() {
10145            if node.kind() == "class_specifier"
10146                && node
10147                    .child_by_field_name("name")
10148                    .is_some_and(|name| node_text(name, source) == expected_name)
10149            {
10150                return Some(node);
10151            }
10152            let mut cursor = node.walk();
10153            stack.extend(node.named_children(&mut cursor));
10154        }
10155        None
10156    }
10157
10158    #[test]
10159    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
10160        let source = r#"EXPORT void definition(struct Value value) {}
10161EXPORT void prototype(struct Value value);
10162"#;
10163        let mut parser = tree_sitter::Parser::new();
10164        parser
10165            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10166            .unwrap();
10167        let tree = parser.parse(source, None).unwrap();
10168        let root = tree.root_node();
10169        let mut cursor = root.walk();
10170        let callables = root
10171            .named_children(&mut cursor)
10172            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
10173            .collect::<Vec<_>>();
10174
10175        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
10176        for callable in callables {
10177            assert!(callable.has_error(), "fixture must exercise error recovery");
10178            assert!(
10179                cpp_sentinel_macro_parts(callable, source).is_none(),
10180                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
10181            );
10182        }
10183    }
10184
10185    #[test]
10186    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
10187        let source = r#"namespace absl {
10188ABSL_NAMESPACE_BEGIN
10189// Generate a floating-point variate conforming to a Beta distribution:
10190template <typename RealType = double>
10191class beta_distribution {
10192 public:
10193  using result_type = RealType;
10194
10195
10196  beta_distribution() : beta_distribution(1) {}
10197
10198  explicit beta_distribution(result_type alpha, result_type beta = 1)
10199      : param_(alpha, beta) {}
10200
10201  explicit beta_distribution(const param_type& p) : param_(p) {}
10202
10203  void reset() {}
10204
10205  // Generating functions
10206  template <typename URBG>
10207  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
10208    return (*this)(g, param_);
10209  }
10210
10211};
10212ABSL_NAMESPACE_END
10213}  // namespace absl
10214"#;
10215        let mut parser = tree_sitter::Parser::new();
10216        parser
10217            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10218            .unwrap();
10219        let tree = parser.parse(source, None).unwrap();
10220        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10221        let body = namespace
10222            .child_by_field_name("body")
10223            .expect("fixture namespace body");
10224        let sentinel = body.named_child(0).expect("sentinel envelope");
10225        let callable = sentinel
10226            .child_by_field_name("declarator")
10227            .and_then(extract_function_declarator)
10228            .and_then(cpp_function_declarator_name_node)
10229            .expect("preserved callable name");
10230
10231        assert_eq!(sentinel.kind(), "function_definition");
10232        assert_eq!(callable.kind(), "operator_name");
10233        assert!(
10234            cpp_sentinel_macro_parts(sentinel, source).is_some(),
10235            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
10236        );
10237    }
10238
10239    #[test]
10240    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
10241        let source = r#"namespace absl {
10242ABSL_NAMESPACE_BEGIN
10243// absl::discrete_distribution
10244//
10245// A discrete distribution produces random integers i, where 0 <= i < n
10246template <typename IntType = int>
10247class discrete_distribution {
10248 public:
10249  using result_type = IntType;
10250  class param_type {
10251   public:
10252    param_type() { init(); }
10253    template <typename InputIterator>
10254    explicit param_type(InputIterator begin, InputIterator end)
10255        : p_(begin, end) {
10256      init();
10257    }
10258  };
10259  discrete_distribution() : param_() {}
10260  explicit discrete_distribution(const param_type& p) : param_(p) {}
10261};
10262ABSL_NAMESPACE_END
10263}  // namespace absl
10264"#;
10265        let mut parser = tree_sitter::Parser::new();
10266        parser
10267            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10268            .unwrap();
10269        let tree = parser.parse(source, None).unwrap();
10270        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
10271        let body = namespace
10272            .child_by_field_name("body")
10273            .expect("fixture namespace body");
10274        let sentinel = body.named_child(0).expect("sentinel envelope");
10275        let callable = sentinel
10276            .child_by_field_name("declarator")
10277            .and_then(extract_function_declarator)
10278            .and_then(cpp_function_declarator_name_node)
10279            .expect("preserved callable name");
10280
10281        assert_eq!(sentinel.kind(), "function_definition");
10282        assert_eq!(callable.kind(), "identifier");
10283        assert!(
10284            cpp_sentinel_macro_parts(sentinel, source).is_some(),
10285            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
10286        );
10287    }
10288
10289    #[test]
10290    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
10291        let source = r#"
10292#define CPPCHECKLIB
10293class Library {
10294    struct Container {
10295        CPPCHECKLIB static std::string toString(Yield yield);
10296        CPPCHECKLIB static std::string toString(Action action);
10297    };
10298};
10299"#;
10300        let mut parser = tree_sitter::Parser::new();
10301        parser
10302            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10303            .unwrap();
10304        let tree = parser.parse(source, None).unwrap();
10305        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
10306        let parsed = parse_cpp_file(&file, source, &tree);
10307        assert!(
10308            parsed
10309                .declarations()
10310                .iter()
10311                .all(|unit| unit.fq_name() != "Library$Container.std"),
10312            "the qualified return-type namespace must not become a field: {:#?}",
10313            parsed.declarations()
10314        );
10315        for expected in ["(Yield)", "(Action)"] {
10316            assert!(
10317                parsed.declarations().iter().any(|unit| {
10318                    unit.is_function()
10319                        && unit.fq_name() == "Library$Container.toString"
10320                        && unit.signature() == Some(expected)
10321                }),
10322                "recovered toString overload {expected} is missing: {:#?}",
10323                parsed.declarations()
10324            );
10325        }
10326    }
10327
10328    #[test]
10329    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
10330        let source = r#"
10331#define SIMPLECPP_LIB
10332namespace simplecpp {
10333using TokenString = std::string;
10334struct Location { int line{}; };
10335class SIMPLECPP_LIB Token {
10336  TokenString prefix;
10337  void prefix_method() {}
10338 public:
10339  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10340      whitespaceahead(wsahead), location(loc), string(s)
10341      // The comment must not hide the constructor body from recovery.
10342      {
10343      flags();
10344  }
10345  TokenString string;
10346  bool whitespaceahead;
10347  Location location;
10348  Token *previous{};
10349 private:
10350  void flags() {
10351      whitespaceahead = true;
10352  }
10353};
10354}
10355"#;
10356        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
10357
10358        let location_fields = parsed
10359            .declarations()
10360            .iter()
10361            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
10362            .collect::<Vec<_>>();
10363        assert_eq!(
10364            location_fields.len(),
10365            1,
10366            "location should have one class-owned declaration: {:#?}",
10367            parsed.declarations()
10368        );
10369        assert!(
10370            location_fields[0].is_field(),
10371            "location has wrong kind: {:#?}",
10372            parsed.declarations()
10373        );
10374        assert!(
10375            parsed.declarations().iter().all(|unit| {
10376                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
10377            })
10378        );
10379        assert!(
10380            parsed.declarations().iter().all(|unit| {
10381                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
10382            })
10383        );
10384        assert!(
10385            parsed
10386                .declarations()
10387                .iter()
10388                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
10389        );
10390        assert!(
10391            parsed
10392                .declarations()
10393                .iter()
10394                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
10395            "the recovered class must retain its constructor: {:#?}",
10396            parsed.declarations()
10397        );
10398        assert!(
10399            parsed
10400                .declarations()
10401                .iter()
10402                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
10403        );
10404        assert!(parsed.declarations().iter().any(|unit| {
10405            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
10406        }));
10407        let constructor = parsed
10408            .declarations()
10409            .iter()
10410            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
10411            .expect("recovered constructor");
10412        let constructor_start = source.find("Token(const").expect("constructor start");
10413        let constructor_end = source
10414            .get(
10415                ..source
10416                    .find("  TokenString string;")
10417                    .expect("constructor end"),
10418            )
10419            .expect("constructor slice")
10420            .trim_end()
10421            .len();
10422        assert!(
10423            parsed
10424                .navigation_ranges
10425                .get(constructor)
10426                .is_some_and(|ranges| {
10427                    ranges.iter().any(|range| {
10428                        range.start_byte == constructor_start && range.end_byte == constructor_end
10429                    })
10430                }),
10431            "constructor navigation must span the full body: {:#?}",
10432            parsed.navigation_ranges
10433        );
10434        assert_eq!(
10435            parsed
10436                .signature_metadata
10437                .get(constructor)
10438                .and_then(|metadata| metadata.first())
10439                .and_then(SignatureMetadata::callable_linkage),
10440            Some(CallableLinkage::External)
10441        );
10442        let token_class = parsed
10443            .declarations()
10444            .iter()
10445            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
10446            .expect("recovered Token class");
10447        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
10448        assert!(
10449            parsed
10450                .navigation_ranges
10451                .get(token_class)
10452                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
10453            "class navigation must include the terminating semicolon: {:#?}",
10454            parsed.navigation_ranges
10455        );
10456    }
10457
10458    #[test]
10459    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
10460        let source = r#"
10461#define SIMPLECPP_LIB
10462namespace simplecpp {
10463using TokenString = std::string;
10464class Macro;
10465struct Location {
10466  unsigned int fileIndex{};
10467  unsigned int line{};
10468  unsigned int col{};
10469};
10470struct Output {
10471  int type;
10472};
10473class SIMPLECPP_LIB Token {
10474 public:
10475  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
10476      whitespaceahead(wsahead), location(loc), string(s) {
10477      flags();
10478  }
10479  Token(const Token &tok) :
10480      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
10481      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
10482      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
10483  Token &operator=(const Token &tok) = delete;
10484  const TokenString& str() const { return string; }
10485  void setstr(const std::string &s) { string = s; flags(); }
10486  bool isOneOf(const char ops[]) const;
10487  TokenString macro;
10488  char op;
10489  bool comment;
10490  bool name;
10491  bool number;
10492  bool whitespaceahead;
10493  Location location;
10494  Token *previous{};
10495  Token *next{};
10496 private:
10497  void flags() {
10498      name = !string.empty();
10499      comment = false;
10500      number = false;
10501      op = 0;
10502  }
10503  TokenString string;
10504};
10505}
10506struct Following {
10507  int type;
10508};
10509class SIMPLECPP_LIB Later {
10510 public:
10511  Later(int value) : value(value) {}
10512  int value;
10513};
10514"#;
10515        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
10516        assert!(
10517            parsed
10518                .declarations()
10519                .iter()
10520                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
10521        );
10522        assert!(
10523            !parsed
10524                .declarations()
10525                .iter()
10526                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
10527        );
10528        assert!(
10529            parsed
10530                .declarations()
10531                .iter()
10532                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
10533        );
10534        assert!(
10535            !parsed
10536                .declarations()
10537                .iter()
10538                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
10539        );
10540        assert!(
10541            parsed
10542                .declarations()
10543                .iter()
10544                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
10545        );
10546        assert!(
10547            parsed
10548                .declarations()
10549                .iter()
10550                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
10551        );
10552        assert!(
10553            parsed
10554                .declarations()
10555                .iter()
10556                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
10557        );
10558        assert!(
10559            parsed
10560                .declarations()
10561                .iter()
10562                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
10563        );
10564        assert!(
10565            parsed
10566                .declarations()
10567                .iter()
10568                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
10569        );
10570        assert!(
10571            parsed
10572                .declarations()
10573                .iter()
10574                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
10575        );
10576        assert!(parsed.declarations().iter().all(|unit| {
10577            !matches!(
10578                unit.fq_name().as_str(),
10579                "simplecpp.Token.Following" | "simplecpp.Token.Later"
10580            )
10581        }));
10582        assert!(
10583            !parsed
10584                .declarations()
10585                .iter()
10586                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
10587            "the following struct must remain outside the recovered Token class"
10588        );
10589    }
10590
10591    #[test]
10592    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
10593        let source = r#"
10594#define SIMPLECPP_LIB
10595namespace {
10596namespace simplecpp {
10597using TokenString = std::string;
10598struct Location { int line{}; };
10599class SIMPLECPP_LIB HiddenToken {
10600 public:
10601  HiddenToken(const TokenString &s, const Location &loc) :
10602      location(loc), string(s) {
10603      flags();
10604  }
10605  TokenString string;
10606  Location location;
10607  HiddenToken *previous{};
10608 private:
10609  void flags() {}
10610};
10611}
10612}
10613"#;
10614        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
10615        let constructor = parsed
10616            .declarations()
10617            .iter()
10618            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
10619            .expect("recovered anonymous-namespace constructor");
10620        assert_eq!(
10621            parsed
10622                .signature_metadata
10623                .get(constructor)
10624                .and_then(|metadata| metadata.first())
10625                .and_then(SignatureMetadata::callable_linkage),
10626            Some(CallableLinkage::Internal)
10627        );
10628    }
10629
10630    #[test]
10631    fn macro_qualified_static_field_keeps_real_declarator() {
10632        let source = r#"#define JSON_INLINE_VARIABLE
10633struct Reader {
10634static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
10635static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
10636static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
10637};"#;
10638        let mut parser = tree_sitter::Parser::new();
10639        parser
10640            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10641            .unwrap();
10642        let tree = parser.parse(source, None).unwrap();
10643        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
10644        let parsed = parse_cpp_file(&file, source, &tree);
10645        for expected in [
10646            "Reader.npos",
10647            "Reader.other",
10648            "Reader.pointer",
10649            "Reader.reference",
10650        ] {
10651            assert!(
10652                parsed
10653                    .declarations()
10654                    .iter()
10655                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
10656                "real macro-decorated field {expected} is missing: {:#?}",
10657                parsed.declarations()
10658            );
10659        }
10660        assert!(
10661            parsed
10662                .declarations()
10663                .iter()
10664                .all(|unit| unit.fq_name() != "Reader.std"),
10665            "qualified type prefix became a pseudo-field: {:#?}",
10666            parsed.declarations()
10667        );
10668        let root = tree.root_node();
10669        let mut stack = vec![root];
10670        let mut signatures = Vec::new();
10671        while let Some(current) = stack.pop() {
10672            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
10673            {
10674                signatures.extend(
10675                    declarators
10676                        .into_iter()
10677                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
10678                );
10679            }
10680            let mut cursor = current.walk();
10681            stack.extend(current.named_children(&mut cursor));
10682        }
10683        signatures.sort();
10684        assert_eq!(
10685            signatures,
10686            [
10687                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
10688                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
10689                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
10690                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
10691            ]
10692        );
10693    }
10694
10695    fn member_function_linkage(source: &str) -> CallableLinkage {
10696        let mut parser = tree_sitter::Parser::new();
10697        parser
10698            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10699            .unwrap();
10700        let tree = parser.parse(source, None).unwrap();
10701        let mut stack = vec![tree.root_node()];
10702        while let Some(node) = stack.pop() {
10703            if node.kind() == "function_definition" {
10704                let mut current = node.parent();
10705                while let Some(parent) = current {
10706                    if matches!(
10707                        parent.kind(),
10708                        "class_specifier" | "struct_specifier" | "union_specifier"
10709                    ) {
10710                        return cpp_callable_linkage(node, source);
10711                    }
10712                    current = parent.parent();
10713                }
10714            }
10715            let mut cursor = node.walk();
10716            stack.extend(node.named_children(&mut cursor));
10717        }
10718        panic!("fixture has no member function definition");
10719    }
10720
10721    #[test]
10722    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
10723        assert_eq!(
10724            member_function_linkage("struct Named { int method() { return 1; } };"),
10725            CallableLinkage::External
10726        );
10727        assert_eq!(
10728            member_function_linkage(
10729                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
10730            ),
10731            CallableLinkage::Internal
10732        );
10733        assert_eq!(
10734            member_function_linkage("struct { int method() { return 1; } } instance;"),
10735            CallableLinkage::Internal
10736        );
10737        assert_eq!(
10738            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
10739            CallableLinkage::Internal
10740        );
10741    }
10742
10743    #[test]
10744    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
10745        let source = r#"
10746#ifndef PROTON_VALUE_HPP
10747#define PROTON_VALUE_HPP
10748namespace proton {
10749namespace internal {
10750class value_base {
10751  protected:
10752    internal::data& data();
10753    internal::data data_;
10754  friend class codec::encoder;
10755  friend class codec::decoder;
10756};
10757}
10758class value : public internal::value_base, private internal::comparable<value> {
10759  private:
10760    template<class T, class U=void> struct assignable :
10761        public std::enable_if<codec::is_encodable<T>::value, U> {};
10762    template<class U> struct assignable<value, U> {};
10763  public:
10764    PN_CPP_EXTERN value();
10765    PN_CPP_EXTERN value(const value&);
10766    PN_CPP_EXTERN value& operator=(const value&);
10767    PN_CPP_EXTERN value(value&&);
10768    PN_CPP_EXTERN value& operator=(value&&);
10769    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
10770    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
10771        codec::encoder e(*this);
10772        e << x;
10773        return *this;
10774    }
10775    PN_CPP_EXTERN type_id type() const;
10776    PN_CPP_EXTERN bool empty() const;
10777    PN_CPP_EXTERN void clear();
10778    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
10779    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
10780  friend PN_CPP_EXTERN void swap(value&, value&);
10781  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
10782  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
10783  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
10784    value(pn_data_t* d);
10785    void reset(pn_data_t* d = 0);
10786};
10787}
10788#endif
10789"#;
10790        let mut parser = tree_sitter::Parser::new();
10791        parser
10792            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10793            .unwrap();
10794        let tree = parser.parse(source, None).unwrap();
10795        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
10796        let parsed = parse_cpp_file(&file, source, &tree);
10797        let macro_constructors = parsed
10798            .signature_metadata
10799            .iter()
10800            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
10801            .flat_map(|(_, metadata)| metadata)
10802            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
10803            .collect::<Vec<_>>();
10804
10805        assert_eq!(
10806            macro_constructors.len(),
10807            3,
10808            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
10809            parsed.declarations()
10810        );
10811        assert!(
10812            macro_constructors.iter().all(|metadata| {
10813                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
10814            }),
10815            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
10816        );
10817    }
10818
10819    #[test]
10820    fn recovered_export_class_typedef_uses_displaced_alias_name() {
10821        let source = r#"
10822namespace spi {
10823class Filter {
10824public:
10825    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
10826};
10827}
10828namespace filter {
10829class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
10830{
10831public:
10832    typedef spi::Filter BASE_CLASS;
10833    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
10834    BEGIN_LOG4CXX_CAST_MAP()
10835    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
10836    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
10837    END_LOG4CXX_CAST_MAP()
10838    FilterDecision decide() const;
10839};
10840}
10841"#;
10842        let mut parser = tree_sitter::Parser::new();
10843        parser
10844            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10845            .unwrap();
10846        let tree = parser.parse(source, None).unwrap();
10847        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
10848        let parsed = parse_cpp_file(&file, source, &tree);
10849        assert!(
10850            parsed.declarations().iter().any(|unit| {
10851                unit.is_class()
10852                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
10853                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
10854            }),
10855            "the displaced typedef alias must retain its declared name: {:#?}",
10856            parsed.declarations()
10857        );
10858        assert!(
10859            parsed
10860                .declarations()
10861                .iter()
10862                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
10863            "the qualified underlying type must not become a false nested alias: {:#?}",
10864            parsed.declarations()
10865        );
10866    }
10867
10868    #[test]
10869    fn exported_single_base_recovery_uses_displaced_class_name() {
10870        let source = r#"
10871class CORE_EXPORT QgsPoint : public AbstractGeometry
10872{
10873    Q_GADGET
10874
10875    Q_PROPERTY( double x READ x WRITE setX )
10876    Q_PROPERTY( double y READ y WRITE setY )
10877    Q_PROPERTY( double z READ z WRITE setZ )
10878    Q_PROPERTY( double m READ m WRITE setM )
10879
10880  public:
10881#ifndef SIP_RUN
10882    QgsPoint(
10883      double x = std::numeric_limits<double>::quiet_NaN(),
10884      double y = std::numeric_limits<double>::quiet_NaN(),
10885      double z = std::numeric_limits<double>::quiet_NaN(),
10886      double m = std::numeric_limits<double>::quiet_NaN(),
10887      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
10888    );
10889#else
10890    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 )];
10891    % MethodCode
10892    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10893    {
10894      int state;
10895      sipIsErr = 0;
10896      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10897      if ( !sipIsErr )
10898      {
10899        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10900      }
10901      sipReleaseType( p, sipType_QgsPointXY, state );
10902    }
10903    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
10904    {
10905      int state;
10906      sipIsErr = 0;
10907
10908      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
10909      if ( !sipIsErr )
10910      {
10911        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
10912      }
10913      sipReleaseType( p, sipType_QPointF, state );
10914    }
10915    else if (
10916      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
10917      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
10918      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
10919      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
10920    {
10921      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
10922      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
10923      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
10924      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
10925      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
10926      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
10927    }
10928    else // Invalid ctor arguments
10929    {
10930      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
10931      sipIsErr = 1;
10932    }
10933    % End
10934#endif
10935
10936    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
10937    explicit QgsPoint( QPointF p ) SIP_SKIP;
10938    explicit QgsPoint(
10939      Qgis::WkbType wkbType,
10940      double x = std::numeric_limits<double>::quiet_NaN(),
10941      double y = std::numeric_limits<double>::quiet_NaN(),
10942      double z = std::numeric_limits<double>::quiet_NaN(),
10943      double m = std::numeric_limits<double>::quiet_NaN()
10944    ) SIP_SKIP;
10945    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10946    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
10947    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
10948#ifndef SIP_RUN
10949  private:
10950    bool fuzzyHelper(
10951      double epsilon,
10952      const AbstractGeometry &other,
10953      bool is3DFlag,
10954      bool isMeasureFlag
10955    ) const
10956    {
10957      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
10958    }
10959#endif
10960};
10961class Ordinary : public Base { public: Ordinary(); };
10962class API_EXPORT Plain { public: Plain(); };
10963class API_EXPORT : public Base {};
10964class
10965PN_CPP_CLASS_EXTERN Sender : public Link {
10966    Sender();
10967};
10968class thread_ctx_t {};
10969class ctx_t ZMQ_FINAL : public thread_ctx_t {
10970    bool start();
10971};
10972"#;
10973        let mut parser = tree_sitter::Parser::new();
10974        parser
10975            .set_language(&tree_sitter_cpp::LANGUAGE.into())
10976            .unwrap();
10977        let tree = parser.parse(source, None).unwrap();
10978        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
10979        let parsed = parse_cpp_file(&file, source, &tree);
10980        let declarations = parsed.declarations();
10981
10982        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
10983            assert!(
10984                declarations
10985                    .iter()
10986                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
10987                "missing recovered class {expected}: {declarations:#?}"
10988            );
10989        }
10990        let qgs_point = declarations
10991            .iter()
10992            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
10993            .expect("recovered QgsPoint class");
10994        assert_eq!(
10995            parsed.raw_supertypes.get(qgs_point),
10996            Some(&vec!["AbstractGeometry".to_string()]),
10997            "single-base export recovery must retain its displaced base"
10998        );
10999        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
11000        assert!(
11001            parsed
11002                .navigation_ranges
11003                .get(qgs_point)
11004                .is_some_and(|ranges| {
11005                    !ranges.is_empty()
11006                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
11007                }),
11008            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
11009            parsed.navigation_ranges.get(qgs_point)
11010        );
11011        let sender = declarations
11012            .iter()
11013            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
11014            .expect("recovered Sender class");
11015        assert_eq!(
11016            parsed.raw_supertypes.get(sender),
11017            Some(&vec!["Link".to_string()]),
11018            "post-declarator export recovery must retain its displaced base"
11019        );
11020        let ctx = declarations
11021            .iter()
11022            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
11023            .expect("recovered ctx_t class");
11024        assert_eq!(
11025            parsed.raw_supertypes.get(ctx),
11026            Some(&vec!["thread_ctx_t".to_string()]),
11027            "postfix export-macro recovery must retain its displaced base"
11028        );
11029        assert!(
11030            declarations.iter().any(|unit| {
11031                unit.is_function()
11032                    && unit.fq_name() == "QgsPoint.QgsPoint"
11033                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
11034            }),
11035            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
11036        );
11037        assert!(
11038            declarations.iter().all(|unit| {
11039                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
11040            }),
11041            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
11042        );
11043    }
11044
11045    #[test]
11046    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
11047        let positive_source =
11048            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
11049        let mut parser = tree_sitter::Parser::new();
11050        parser
11051            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11052            .unwrap();
11053        let positive_tree = parser.parse(positive_source, None).unwrap();
11054        assert!(cpp_reparsed_members_are_indexable(
11055            positive_tree.root_node(),
11056            positive_source
11057        ));
11058
11059        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
11060        let negative_tree = parser.parse(negative_source, None).unwrap();
11061        assert!(!cpp_reparsed_members_are_indexable(
11062            negative_tree.root_node(),
11063            negative_source
11064        ));
11065    }
11066
11067    #[test]
11068    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
11069        let copy_control_source = r#"
11070public:
11071    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
11072    explicit Token(const Token* tok);
11073    ~Token();
11074    Token* astOperand1() { return nullptr; }
11075"#;
11076        let constraint_source = r#"
11077private:
11078    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11079    static T *tokAtImpl(T *tok, int index) {
11080        return tok;
11081    }
11082
11083    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
11084    static T *linkAtImpl(T *tok, int index) {
11085        return tok;
11086    }
11087
11088public:
11089    int late() const { return 1; }
11090"#;
11091        let mut parser = tree_sitter::Parser::new();
11092        parser
11093            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11094            .unwrap();
11095        let copy_control_tree = parser
11096            .parse(copy_control_source, None)
11097            .expect("parse copy-control fixture");
11098        assert!(
11099            copy_control_tree.root_node().has_error(),
11100            "fixture must exercise adjacent copy-control recovery"
11101        );
11102        assert!(
11103            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
11104            "a complete late getter must remain recoverable after adjacent copy-control declarations"
11105        );
11106        let mut cursor = copy_control_tree.root_node().walk();
11107        assert!(
11108            copy_control_tree
11109                .root_node()
11110                .named_children(&mut cursor)
11111                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
11112            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
11113            copy_control_tree.root_node().to_sexp()
11114        );
11115        let constraint_tree = parser
11116            .parse(constraint_source, None)
11117            .expect("parse constraint-macro fixture");
11118        assert!(constraint_tree.root_node().has_error());
11119        assert!(
11120            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
11121            "complete constraint-macro members must not hide a later ordinary member"
11122        );
11123        let mut cursor = constraint_tree.root_node().walk();
11124        assert!(
11125            constraint_tree
11126                .root_node()
11127                .named_children(&mut cursor)
11128                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
11129                    child,
11130                    constraint_source
11131                )),
11132            "fixture must retain the split constraint-macro prefix/function geometry"
11133        );
11134    }
11135
11136    #[test]
11137    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
11138        let source = r#"
11139struct Analyzer {
11140    struct Action {
11141        Action() = default;
11142        Action(const Action&) = default;
11143        Action& operator=(const Action& rhs) & = default;
11144
11145        template<class T,
11146                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
11147                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
11148        // NOLINTNEXTLINE(google-explicit-constructor)
11149        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
11150        {}
11151
11152        enum : std::uint16_t { None = 0, Read = (1 << 0) };
11153        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
11154
11155    private:
11156        unsigned int mFlag{};
11157    };
11158
11159    enum class Direction : unsigned char { Forward, Reverse };
11160    virtual Action analyze(Direction d) const = 0;
11161};
11162"#;
11163        let mut parser = tree_sitter::Parser::new();
11164        parser
11165            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11166            .unwrap();
11167        let tree = parser.parse(source, None).unwrap();
11168        assert!(tree.root_node().has_error());
11169        let root = tree.root_node();
11170        let outer = root
11171            .named_children(&mut root.walk())
11172            .find(|child| child.kind() == "ERROR")
11173            .expect("fragmented Analyzer prefix");
11174        let (outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
11175            .expect("structured Analyzer fragment boundary");
11176        assert_eq!(outer_name, "Analyzer");
11177        let outer_tree = cpp_reparse_fragmented_class_body(
11178            source,
11179            outer_fragment.reparse_start,
11180            outer_fragment.reparse_end,
11181        )
11182        .expect("reparse Analyzer body");
11183        let outer_root = outer_tree.root_node();
11184        let action_prefix = outer_root
11185            .named_children(&mut outer_root.walk())
11186            .find(|child| child.kind() == "ERROR")
11187            .expect("fragmented Action prefix");
11188        let (action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
11189            .expect("structured Action fragment boundary");
11190        assert_eq!(action_name, "Action");
11191        let action_tree = cpp_reparse_fragmented_class_body(
11192            source,
11193            action_fragment.reparse_start,
11194            action_fragment.reparse_end,
11195        )
11196        .expect("reparse Action body");
11197        let action_root = action_tree.root_node();
11198        let macro_prefix = action_root
11199            .named_children(&mut action_root.walk())
11200            .find(|child| child.kind() == "ERROR")
11201            .expect("constraint macro prefix");
11202        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
11203            .expect("structured template macro prefix");
11204        let macro_companion =
11205            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
11206        assert!(
11207            cpp_reparsed_template_macro_constructor_companion_is_indexable(
11208                macro_companion,
11209                macro_parameter,
11210                source,
11211            ),
11212            "split constrained constructor must be admitted: {}",
11213            macro_companion.to_sexp()
11214        );
11215        assert!(
11216            cpp_reparsed_members_are_indexable(action_root, source),
11217            "complete Action body must pass the recovery gate: {}",
11218            action_tree.root_node().to_sexp()
11219        );
11220        assert!(
11221            cpp_reparsed_members_are_indexable(outer_root, source),
11222            "complete Analyzer body must pass the recovery gate: {}",
11223            outer_tree.root_node().to_sexp()
11224        );
11225        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
11226        let parsed = parse_cpp_file(&file, source, &tree);
11227        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
11228            assert!(
11229                parsed
11230                    .declarations()
11231                    .iter()
11232                    .any(|unit| unit.fq_name() == expected),
11233                "missing recovered declaration {expected}: {:#?}",
11234                parsed.declarations()
11235            );
11236        }
11237        assert!(
11238            parsed
11239                .declarations()
11240                .iter()
11241                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
11242            "nested members must not remain flattened: {:#?}",
11243            parsed.declarations()
11244        );
11245    }
11246
11247    #[test]
11248    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
11249        let source = r#"
11250raw_hash_set& operator=(raw_hash_set&& that) {
11251  return move_assign(
11252      std::move(that),
11253      typename AllocTraits::propagate_on_container_move_assignment());
11254}
11255
11256iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
11257  return {};
11258}
11259
11260void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
11261
11262iterator insert(const_iterator hint, value_type&& value)
11263    ABSL_ATTRIBUTE_LIFETIME_BOUND {
11264  return {};
11265}
11266
11267friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
11268  return left.size() == right.size();
11269}
11270
11271static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
11272  return static_cast<slot_type*>(buffer);
11273}
11274
11275protected:
11276// Included-range recovery can attach this comment to the template prefix.
11277template <class K>
11278void AssertOnFind([[maybe_unused]] const K& key) {
11279  Check(key);
11280}
11281"#;
11282        let mut parser = tree_sitter::Parser::new();
11283        parser
11284            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11285            .unwrap();
11286        let tree = parser.parse(source, None).unwrap();
11287        assert!(
11288            tree.root_node().has_error(),
11289            "the fixture must exercise tree-sitter's errorful member shapes"
11290        );
11291        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
11292
11293        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
11294        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
11295        assert!(!cpp_reparsed_members_are_indexable(
11296            incomplete_tree.root_node(),
11297            incomplete_source
11298        ));
11299
11300        let outside_error_source = "int foo() stray_attribute {}\n";
11301        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
11302        assert!(outside_error_tree.root_node().has_error());
11303        assert!(!cpp_reparsed_members_are_indexable(
11304            outside_error_tree.root_node(),
11305            outside_error_source
11306        ));
11307
11308        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
11309        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11310        assert!(!cpp_reparsed_members_are_indexable(
11311            variable_initializer_tree.root_node(),
11312            variable_initializer_source
11313        ));
11314    }
11315
11316    #[test]
11317    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
11318        let positive_source = r#"
11319std::pair<iterator, bool> insert(init_type&& value)
11320    ABSL_ATTRIBUTE_LIFETIME_BOUND
11321#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11322  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11323#endif
11324{
11325  return emplace(std::move(value));
11326}
11327"#;
11328        let mut parser = tree_sitter::Parser::new();
11329        parser
11330            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11331            .unwrap();
11332        let positive_tree = parser.parse(positive_source, None).unwrap();
11333        assert!(
11334            positive_tree.root_node().has_error(),
11335            "the fixture must exercise the split attribute/requires shape"
11336        );
11337        assert!(cpp_reparsed_members_are_indexable(
11338            positive_tree.root_node(),
11339            positive_source
11340        ));
11341
11342        let template_return_source = r#"
11343pair<int> insert(init_type&& value)
11344    ABSL_ATTRIBUTE_LIFETIME_BOUND
11345#if LANGUAGE_LEVEL >= 202002L
11346  requires(!Predicate<init_type>::value)
11347#endif
11348// Attributes and the function body may be separated by comments.
11349{
11350  return {};
11351}
11352"#;
11353        let template_return_tree = parser.parse(template_return_source, None).unwrap();
11354        assert!(
11355            cpp_reparsed_members_are_indexable(
11356                template_return_tree.root_node(),
11357                template_return_source
11358            ),
11359            "template-return attribute/requires tree: {}",
11360            template_return_tree.root_node().to_sexp()
11361        );
11362
11363        let no_body_source = r#"
11364std::pair<iterator, bool> insert(init_type&& value)
11365    ABSL_ATTRIBUTE_LIFETIME_BOUND
11366#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
11367  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
11368#endif
11369+ 0;
11370"#;
11371        let no_body_tree = parser.parse(no_body_source, None).unwrap();
11372        assert!(!cpp_reparsed_members_are_indexable(
11373            no_body_tree.root_node(),
11374            no_body_source
11375        ));
11376
11377        let extra_payload_source = r#"
11378pair<int> insert(init_type&& value)
11379    ABSL_ATTRIBUTE_LIFETIME_BOUND
11380#if LANGUAGE_LEVEL >= 202002L
11381  int unrelated;
11382  requires(Predicate<init_type>::value)
11383#endif
11384{
11385  return {};
11386}
11387"#;
11388        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
11389        assert!(!cpp_reparsed_members_are_indexable(
11390            extra_payload_tree.root_node(),
11391            extra_payload_source
11392        ));
11393
11394        let variable_initializer_source = r#"
11395int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
11396#if LANGUAGE_LEVEL >= 202002L
11397  requires(true)
11398#endif
11399{
11400  bad;
11401}
11402"#;
11403        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
11404        assert!(!cpp_reparsed_members_are_indexable(
11405            variable_initializer_tree.root_node(),
11406            variable_initializer_source
11407        ));
11408    }
11409
11410    #[test]
11411    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
11412        let source = r#"namespace absl {
11413ABSL_NAMESPACE_BEGIN namespace container_internal {
11414template <class Policy>
11415class raw_hash_set {
11416 public:
11417  T operator->() const { return &operator*(); }
11418  template <bool do_destroy>
11419  struct InsertSlot {
11420    raw_hash_set& s;
11421    int invoke();
11422  };
11423  int tail;
11424};
11425}
11426ABSL_NAMESPACE_END
11427}"#;
11428        let mut parser = tree_sitter::Parser::new();
11429        parser
11430            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11431            .unwrap();
11432        let tree = parser.parse(source, None).unwrap();
11433        let field = "    raw_hash_set& s;";
11434        let start = source.find(field).expect("InsertSlot field") + 4;
11435        let node = tree
11436            .root_node()
11437            .descendant_for_byte_range(start, start + "raw_hash_set".len())
11438            .expect("raw_hash_set type node");
11439        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11440
11441        assert_eq!(
11442            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
11443            Some(vec![
11444                "absl".to_string(),
11445                "container_internal".to_string(),
11446                "raw_hash_set".to_string(),
11447                "InsertSlot".to_string(),
11448            ])
11449        );
11450    }
11451
11452    #[test]
11453    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
11454        const DISTINCT_PER_KIND: usize = 64;
11455        let mut source = String::new();
11456        for index in 0..DISTINCT_PER_KIND {
11457            writeln!(source, "typedef int Alias{index};").unwrap();
11458        }
11459        writeln!(source, "typedef long Alias0;").unwrap();
11460        for index in 0..DISTINCT_PER_KIND {
11461            writeln!(source, "#define MACRO_{index} {index}").unwrap();
11462        }
11463        writeln!(source, "#define MACRO_0 duplicate").unwrap();
11464        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
11465
11466        let mut parser = tree_sitter::Parser::new();
11467        parser
11468            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11469            .unwrap();
11470        let tree = parser.parse(&source, None).unwrap();
11471        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
11472
11473        start_declaration_identity_comparison_probe();
11474        let parsed = parse_cpp_file(&file, &source, &tree);
11475        let comparisons = finish_declaration_identity_comparison_probe();
11476
11477        assert_eq!(
11478            DISTINCT_PER_KIND + 1,
11479            parsed
11480                .declarations()
11481                .iter()
11482                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
11483                .count(),
11484            "every physical typedef alias declaration must be retained so \
11485             conditional branch guards stay available to the resolver"
11486        );
11487        assert_eq!(
11488            DISTINCT_PER_KIND,
11489            parsed
11490                .declarations()
11491                .iter()
11492                .filter(|unit| {
11493                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
11494                })
11495                .count(),
11496            "macros should retain semantic-identity deduplication"
11497        );
11498        assert_eq!(
11499            2,
11500            parsed
11501                .declarations()
11502                .iter()
11503                .filter(|unit| {
11504                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
11505                })
11506                .count(),
11507            "function overloads must remain distinct"
11508        );
11509
11510        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
11511        assert!(
11512            comparisons <= dedup_inputs * 4,
11513            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
11514        );
11515    }
11516
11517    #[test]
11518    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
11519        let source = r#"namespace absl {
11520ABSL_NAMESPACE_BEGIN namespace container_internal {
11521template <typename T>
11522class broken {
11523 public:
11524  using value_type = T;
11525  T operator->() const { return &operator*(); }
11526  using alias = value_type;
11527};
11528}
11529}
11530"#;
11531        let mut parser = tree_sitter::Parser::new();
11532        parser
11533            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11534            .unwrap();
11535        let tree = parser.parse(source, None).unwrap();
11536        let broken = find_class_named(tree.root_node(), source, "broken")
11537            .expect("the positive fixture must expose the broken class node");
11538        assert!(
11539            broken.has_error(),
11540            "the positive fixture must retain an internal parser error"
11541        );
11542        assert!(
11543            cpp_complete_class_body_close(broken).is_some(),
11544            "the positive fixture must expose a real class body close"
11545        );
11546        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11547        assert!(
11548            recovered.iter().any(|class| {
11549                class.scope_components == ["absl", "container_internal", "broken"]
11550            }),
11551            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
11552        );
11553    }
11554
11555    #[test]
11556    fn sentinel_recovery_keeps_members_after_nested_body_close() {
11557        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
11558NLOHMANN_BASIC_JSON_TPL_DECLARATION
11559class basic_json {
11560 private:
11561  union storage {
11562    int value;
11563  } data;
11564 public:
11565  using late_alias = int;
11566  late_alias value() const;
11567};
11568NLOHMANN_JSON_NAMESPACE_END
11569"#;
11570        let mut parser = tree_sitter::Parser::new();
11571        parser
11572            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11573            .unwrap();
11574        let tree = parser.parse(source, None).unwrap();
11575        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11576        let basic_json = recovered
11577            .iter()
11578            .find(|class| {
11579                class
11580                    .scope_components
11581                    .last()
11582                    .is_some_and(|name| name == "basic_json")
11583            })
11584            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
11585        let late_alias = source
11586            .find("late_alias value")
11587            .expect("late alias reference");
11588        assert!(
11589            basic_json.class_range.start_byte < late_alias
11590                && late_alias < basic_json.class_range.end_byte,
11591            "the recovered class range must include members after a nested close: {basic_json:#?}"
11592        );
11593    }
11594
11595    #[test]
11596    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
11597        let source = r#"namespace absl {
11598ABSL_NAMESPACE_BEGIN namespace container_internal {
11599template <typename T>
11600class broken {
11601 public:
11602  using value_type = T;
11603  T operator->() const { return &operator*(); }
11604}
11605}
11606"#;
11607        let mut parser = tree_sitter::Parser::new();
11608        parser
11609            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11610            .unwrap();
11611        let tree = parser.parse(source, None).unwrap();
11612        let broken = find_class_named(tree.root_node(), source, "broken")
11613            .expect("the negative fixture must expose the malformed class node");
11614        assert!(
11615            broken.has_error(),
11616            "the negative fixture must retain a parser error"
11617        );
11618        assert!(
11619            cpp_complete_class_body_close(broken).is_none(),
11620            "the malformed class must not expose a real body close"
11621        );
11622        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11623        assert!(
11624            recovered
11625                .iter()
11626                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
11627            "an incomplete class must not borrow the namespace close: {recovered:#?}"
11628        );
11629    }
11630
11631    #[test]
11632    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
11633        let source = r#"namespace absl {
11634ABSL_NAMESPACE_BEGIN namespace container_internal {
11635template <typename T>
11636struct broken {
11637  using value_type = T;
11638};
11639}
11640
11641#ifdef OWNER_DEF
11642template <typename T>
11643typename broken<T>::value_type broken<T>::method() {
11644  value_type value{};
11645  return value;
11646}
11647#endif
11648
11649namespace sibling {
11650template <typename T>
11651typename broken<T>::value_type broken<T>::other() {
11652  value_type value{};
11653  return value;
11654}
11655}
11656
11657ABSL_NAMESPACE_END
11658}
11659"#;
11660        let mut parser = tree_sitter::Parser::new();
11661        parser
11662            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11663            .unwrap();
11664        let tree = parser.parse(source, None).unwrap();
11665        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11666        let broken = recovered
11667            .iter()
11668            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11669            .expect("the sentinel class must be recovered");
11670        let method_start = source
11671            .find("typename broken<T>::value_type broken<T>::method()")
11672            .expect("guarded sibling owner");
11673        let method_end = source[method_start..]
11674            .find("\n}")
11675            .map(|offset| method_start + offset + 2)
11676            .expect("guarded sibling owner close");
11677        assert!(
11678            broken
11679                .owner_ranges
11680                .iter()
11681                .any(|owner| owner.range.start_byte <= method_start
11682                    && method_end <= owner.range.end_byte),
11683            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
11684        );
11685        let sibling_start = source
11686            .find("typename broken<T>::value_type broken<T>::other()")
11687            .expect("nested namespace sibling owner");
11688        assert!(
11689            broken
11690                .owner_ranges
11691                .iter()
11692                .all(|owner| owner.range.start_byte > sibling_start
11693                    || owner.range.end_byte <= sibling_start),
11694            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
11695        );
11696    }
11697
11698    #[test]
11699    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
11700        let source = r#"#ifdef OUTER
11701namespace absl {
11702ABSL_NAMESPACE_BEGIN namespace container_internal {
11703template <typename T>
11704struct broken {
11705  using value_type = T;
11706};
11707}
11708}
11709
11710#ifdef OWNER_DEF
11711template <typename T>
11712typename broken<T>::value_type broken<T>::method() {
11713  value_type value{};
11714  return value;
11715}
11716#endif
11717#endif
11718"#;
11719        let mut parser = tree_sitter::Parser::new();
11720        parser
11721            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11722            .unwrap();
11723        let tree = parser.parse(source, None).unwrap();
11724        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
11725        let broken = recovered
11726            .iter()
11727            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
11728            .expect("the sentinel class must be recovered");
11729        let method_start = source
11730            .find("typename broken<T>::value_type broken<T>::method()")
11731            .expect("outer sibling owner");
11732        assert!(
11733            broken
11734                .owner_ranges
11735                .iter()
11736                .all(|owner| owner.range.start_byte > method_start
11737                    || owner.range.end_byte <= method_start),
11738            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
11739        );
11740    }
11741
11742    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
11743    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
11744        let mut signatures = parsed
11745            .declarations()
11746            .iter()
11747            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
11748            .filter_map(|unit| unit.signature().map(str::to_string))
11749            .collect::<Vec<_>>();
11750        signatures.sort();
11751        signatures.dedup();
11752        signatures
11753    }
11754
11755    #[test]
11756    fn trailing_qualifiers_survive_parameter_list_whitespace() {
11757        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
11758        // declarator's structure, so an out-of-line definition that spells its
11759        // parameter list with different whitespace than the declaration must
11760        // still carry it.
11761        let source = r#"
11762struct Widget {
11763  bool multiline(int settings, int supprs) const;
11764  bool doublespace(int settings, int supprs) const;
11765  bool noexcept_multiline(int settings, int supprs) noexcept;
11766  bool ref_multiline(int settings, int supprs) &&;
11767};
11768bool
11769Widget::multiline (int settings,
11770                   int supprs) const
11771{ return settings + supprs > 0; }
11772bool Widget::doublespace(int settings,  int supprs) const { return true; }
11773bool Widget::noexcept_multiline(int settings,
11774                                int supprs) noexcept { return true; }
11775bool Widget::ref_multiline(int settings,
11776                           int supprs) && { return true; }
11777"#;
11778        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
11779        assert_eq!(
11780            vec!["(int, int) const".to_string()],
11781            identity_signatures(&parsed, "Widget.multiline")
11782        );
11783        assert_eq!(
11784            vec!["(int, int) const".to_string()],
11785            identity_signatures(&parsed, "Widget.doublespace")
11786        );
11787        assert_eq!(
11788            vec!["(int, int) noexcept".to_string()],
11789            identity_signatures(&parsed, "Widget.noexcept_multiline")
11790        );
11791        assert_eq!(
11792            vec!["(int, int) &&".to_string()],
11793            identity_signatures(&parsed, "Widget.ref_multiline")
11794        );
11795    }
11796
11797    #[test]
11798    fn trailing_qualifiers_still_separate_genuine_overloads() {
11799        // The qualifier must keep distinguishing the real C++ overload sets it
11800        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
11801        let source = r#"
11802struct Widget {
11803  int* slot(int index);
11804  const int* slot(int index) const;
11805  int log(int severity) &;
11806  int log(int severity) &&;
11807};
11808"#;
11809        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
11810        assert_eq!(
11811            vec!["(int)".to_string(), "(int) const".to_string()],
11812            identity_signatures(&parsed, "Widget.slot")
11813        );
11814        assert_eq!(
11815            vec!["(int) &".to_string(), "(int) &&".to_string()],
11816            identity_signatures(&parsed, "Widget.log")
11817        );
11818    }
11819
11820    #[test]
11821    fn virtual_specifier_is_not_part_of_the_identity_signature() {
11822        // `override` never appears on the out-of-line definition, and C++ does
11823        // not make it part of the signature, so it must not split the identity.
11824        let source = r#"
11825struct Base {
11826  virtual void run(int value) const;
11827};
11828struct Widget : Base {
11829  void run(int value) const override;
11830};
11831void Widget::run(int value) const {}
11832"#;
11833        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
11834        assert_eq!(
11835            vec!["(int) const".to_string()],
11836            identity_signatures(&parsed, "Widget.run")
11837        );
11838    }
11839
11840    #[test]
11841    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
11842        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
11843        // the function type, so a declaration that spells `const int` and a
11844        // definition that spells `int` are one entity.
11845        let source = r#"
11846struct Widget {
11847  bool value_params(const int settings, const int supprs);
11848  void pointee_const(const int* p);
11849  void pointer_const(int* const p);
11850  void both_const(const int* const p);
11851  void reference_const(const int& p);
11852  void array_const(const int values[4]);
11853};
11854bool Widget::value_params(int settings, int supprs) { return true; }
11855void Widget::pointer_const(int* p) {}
11856void Widget::both_const(const int* p) {}
11857"#;
11858        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
11859        assert_eq!(
11860            vec!["(int, int)".to_string()],
11861            identity_signatures(&parsed, "Widget.value_params")
11862        );
11863        assert_eq!(
11864            vec!["(int *)".to_string()],
11865            identity_signatures(&parsed, "Widget.pointer_const")
11866        );
11867        assert_eq!(
11868            vec!["(const int *)".to_string()],
11869            identity_signatures(&parsed, "Widget.both_const")
11870        );
11871        // The const that is not top-level still distinguishes the type.
11872        assert_eq!(
11873            vec!["(const int *)".to_string()],
11874            identity_signatures(&parsed, "Widget.pointee_const")
11875        );
11876        assert_eq!(
11877            vec!["(const int &)".to_string()],
11878            identity_signatures(&parsed, "Widget.reference_const")
11879        );
11880        assert_eq!(
11881            vec!["(const int [4])".to_string()],
11882            identity_signatures(&parsed, "Widget.array_const")
11883        );
11884    }
11885
11886    #[test]
11887    fn top_level_parameter_const_still_separates_pointee_overloads() {
11888        let source = r#"
11889struct Widget {
11890  void take(const int* p);
11891  void take(int* p);
11892};
11893"#;
11894        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
11895        assert_eq!(
11896            vec!["(const int *)".to_string(), "(int *)".to_string()],
11897            identity_signatures(&parsed, "Widget.take")
11898        );
11899    }
11900}