Skip to main content

brokk_bifrost_cpp/
declarations.rs

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