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