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