Skip to main content

brokk_bifrost_cpp/
declarations.rs

1//! The C++ declaration walk, including the macro-sentinel error recovery.
2//!
3//! Every function here is a pure function of a parsed tree and its source text.
4//! `analyzer/cpp/adapter.rs` in `brokk-bifrost-analysis` drives
5//! [`CppVisitor`] out of `LanguageAdapter::parse_file`.
6
7use brokk_bifrost_core::analyzer::common::{node_source_text, parse_source_region};
8use brokk_bifrost_core::analyzer::fq_name::{FqName, SegmentId, SegmentKind, segment_interner};
9use brokk_bifrost_core::analyzer::model::{
10    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
11    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
12    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
13    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
14    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
15};
16use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
17use brokk_bifrost_core::analyzer::structural::materialization::{
18    GenerationKind, MaterializationRecord,
19};
20use brokk_bifrost_core::analyzer::tree_walk::{WalkControl, walk_named_tree_preorder};
21use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
22use brokk_bifrost_core::hash::{HashMap, HashSet};
23use regex::Regex;
24use tree_sitter::{Node, Parser, Tree};
25
26/// Intern one qualified-name segment in the process-global interner.
27fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
28    segment_interner().intern(text, kind)
29}
30
31/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
32/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
33/// `::` head is exactly the mixed-separator store issue #1163 is about; the
34/// structured form records each namespace component, and the equivalence check
35/// renders it natively (with `::` between adjacent Package segments) so it
36/// round-trips to the legacy string. Splitting the already-joined string here is
37/// the M1 bridge — the legacy strings stay authoritative until M3.
38fn cpp_push_package(fq: &mut FqName, package_name: &str) {
39    for component in package_name.split("::").filter(|c| !c.is_empty()) {
40        fq.push(cpp_segment(component, SegmentKind::Package));
41    }
42}
43
44/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
45/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
46/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
47/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
48/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
49/// cpp-specific native rendering rule is needed for this chain.
50fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
51    let mut first = true;
52    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
53    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
54    // entry point, not re-inference of an already-structured name.
55    for component in chain.split('$').filter(|c| !c.is_empty()) {
56        let kind = if first {
57            SegmentKind::Type
58        } else {
59            SegmentKind::Nested
60        };
61        fq.push(cpp_segment(component, kind));
62        first = false;
63    }
64}
65
66/// Structured name for a C++ namespace module: every `::`-separated component is
67/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
68/// `short_name` with an empty `package_name`).
69fn cpp_namespace_fq(full_name: &str) -> FqName {
70    let mut fq = FqName::new();
71    cpp_push_package(&mut fq, full_name);
72    fq
73}
74
75/// The per-level namespace components a `namespace_definition`'s `name` field
76/// declares.
77///
78/// A C++17 nested definition (`namespace a::b::c`) parses as a
79/// `nested_namespace_specifier` whose named children are the per-level
80/// `namespace_identifier`s plus, for three or more levels, a further
81/// `nested_namespace_specifier`; the `::` separators, the optional per-level
82/// `inline`, and the leading global `::` are all anonymous tokens the walk
83/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
84/// segment path as the expanded `namespace a { namespace b { } }` form.
85///
86/// A shape outside that grammar is the deliberately ill-formed source the
87/// diagnostic corpora carry. Those keep their historical single-component
88/// reading of the raw name text, which the caller still joins to the lexical
89/// namespace exactly as before.
90fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
91    let mut components = Vec::new();
92    let mut stack = vec![node];
93    while let Some(current) = stack.pop() {
94        match current.kind() {
95            "namespace_identifier" | "identifier" => {
96                components.push(normalize_cpp_whitespace(node_text(current, source)));
97            }
98            "nested_namespace_specifier" => {
99                for index in (0..current.named_child_count()).rev() {
100                    stack.push(
101                        current
102                            .named_child(index)
103                            .expect("index below the node's own named child count"),
104                    );
105                }
106            }
107            _ => return cpp_raw_namespace_name_components(node, source),
108        }
109    }
110    if components.iter().any(String::is_empty) {
111        return cpp_raw_namespace_name_components(node, source);
112    }
113    components
114}
115
116/// The historical reading of a namespace name node: its whole source text as
117/// one component, with a leading global `::` marker dropped so the caller's
118/// global-scope handling stays the AST boundary rather than a text prefix.
119fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
120    let start = node
121        .child(0)
122        .filter(|child| !child.is_named() && child.kind() == "::")
123        .map_or(node.start_byte(), |marker| marker.end_byte());
124    let text = normalize_cpp_whitespace(
125        source
126            .get(start..node.end_byte())
127            .expect("namespace name node covers one source range"),
128    );
129    if text.is_empty() {
130        return Vec::new();
131    }
132    vec![text]
133}
134
135/// Return the named namespace path that structurally encloses `node`.
136///
137/// This intentionally follows namespace AST ancestors rather than inspecting
138/// source text. Anonymous namespaces are not representable in the legacy C++
139/// package field, so a path containing one fails closed.
140fn cpp_lexical_namespace_name(node: Node<'_>, source: &str) -> Option<String> {
141    let mut components = Vec::new();
142    let mut ancestor = node.parent();
143    while let Some(current) = ancestor {
144        if current.kind() == "namespace_definition" {
145            let name_node = current.child_by_field_name("name")?;
146            let name = normalize_cpp_whitespace(node_text(name_node, source));
147            if name.is_empty() {
148                return None;
149            }
150            components.push(name);
151        }
152        ancestor = current.parent();
153    }
154    if components.is_empty() {
155        return None;
156    }
157    components.reverse();
158    Some(components.join("::"))
159}
160
161/// Nested-class `$` join for short names. An anonymous parent class (empty
162/// short_name) contributes no segment: the FqName bridge drops empty
163/// components, so a bare `parent$child` join would desync `short_name` from
164/// the fq and trip the package/short boundary assert in
165/// `CodeUnit::with_signature_and_fq` (#2140).
166fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
167    if parent_short.is_empty() {
168        name.to_string()
169    } else {
170        format!("{parent_short}${name}")
171    }
172}
173
174/// Member `.` join for short names; same anonymous-parent guard as
175/// [`cpp_join_nested_short`] (#2140).
176fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
177    if parent_short.is_empty() {
178        name.to_string()
179    } else {
180        format!("{parent_short}.{name}")
181    }
182}
183
184/// Structural fq for a leaf declaration: the parent unit's fq plus this
185/// declaration's own name as one segment (or the package segments plus the
186/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
187/// chain, so a literal `$` inside a source identifier (Cython template
188/// substitution points, gcc `$`-identifiers) survives instead of corrupting
189/// the chain and tripping the package/short boundary assert (#2140).
190fn cpp_leaf_fq(
191    package_name: &str,
192    parent: Option<&CodeUnit>,
193    name: &str,
194    kind_if_nested: SegmentKind,
195    kind_if_top: SegmentKind,
196) -> FqName {
197    if let Some(parent) = parent {
198        parent
199            .fq()
200            .clone()
201            .with_pushed(cpp_segment(name, kind_if_nested))
202    } else {
203        let mut fq = FqName::new();
204        cpp_push_package(&mut fq, package_name);
205        fq.push(cpp_segment(name, kind_if_top));
206        fq
207    }
208}
209
210/// Structured name for a member unit (function, field, enumerator). The
211/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
212/// the member has an owner, by `.member`; free functions and globals have no
213/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
214/// C++ member names never contain a literal `.`, so the single `.` (if any)
215/// separates the owner chain from the member.
216pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
217    let mut fq = FqName::new();
218    cpp_push_package(&mut fq, package_name);
219    match short_name.rsplit_once('.') {
220        Some((owner_chain, member)) => {
221            cpp_push_type_chain(&mut fq, owner_chain);
222            fq.push(cpp_segment(member, SegmentKind::Member));
223        }
224        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
225    }
226    fq
227}
228
229#[derive(Clone)]
230pub struct ScopeInfo {
231    package_name: String,
232    module: Option<CodeUnit>,
233    class_unit: Option<CodeUnit>,
234    template_signature: Option<String>,
235    template_metadata: Option<CppTemplateMetadata>,
236    declarations_are_fields: bool,
237    recovered_specialization_member_scope: bool,
238    /// Namespace targets of every `using namespace X;` directive lexically
239    /// visible at this point in the file (declaration order), threaded
240    /// forward sibling-by-sibling by the sequential container walk (see
241    /// `CppWork::Siblings`). An out-of-line member definition written as a
242    /// bare `Class::method` at file/namespace scope with no enclosing
243    /// `namespace {}` block (issue #1093, e.g. log4cxx's
244    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
245    /// const { ... }`) has no other structural signal for which namespace
246    /// actually owns `Class`; this is the best-effort candidate list used to
247    /// recover it so the definition's indexed identity matches its header
248    /// declaration's.
249    visible_using_namespaces: Vec<String>,
250}
251
252struct CppContainer<'tree> {
253    node: Node<'tree>,
254    scope: ScopeInfo,
255}
256
257struct CppNodeWork<'tree> {
258    node: Node<'tree>,
259    scope: ScopeInfo,
260}
261
262/// Cursor over one container's remaining named children, processed one at a
263/// time (rather than all at once) so a `using namespace X;` sibling can
264/// update `scope.visible_using_namespaces` for the siblings that follow it,
265/// matching real C++ using-directive semantics. Nested container work is
266/// still pushed and fully drained before the cursor resumes (stack LIFO
267/// order), preserving the original left-to-right visitation order.
268struct CppSiblingsWork<'tree> {
269    children: std::vec::IntoIter<Node<'tree>>,
270    scope: ScopeInfo,
271}
272
273enum CppWork<'tree> {
274    Container(CppContainer<'tree>),
275    Node(CppNodeWork<'tree>),
276    Siblings(CppSiblingsWork<'tree>),
277}
278
279fn class_like_name(node: Node<'_>, source: &str) -> Option<String> {
280    let best = class_like_name_from_children(node, source);
281    if let Some(parent) = node.parent()
282        && matches!(
283            parent.kind(),
284            "declaration" | "field_declaration" | "function_definition"
285        )
286        // A class_specifier carrying its own body proves the grammar name is
287        // the real class name: a sibling declarator then declares an object
288        // (`class X {} x;`), never a displaced class name. The gate matters
289        // when the class name is itself an all-caps token (`X`, `API`) --
290        // without it the export-macro re-read below steals the object
291        // declarator's name for the class (#2283). The genuine export-macro
292        // shapes leave the class_specifier bodyless, the same invariant
293        // recover_malformed_exported_multiple_base_class already gates on.
294        && cpp_body_node(node).is_none()
295        && node
296            .child_by_field_name("name")
297            .map(|name_node| {
298                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
299            })
300            .unwrap_or(false)
301        && let Some(recovered) = exported_class_name_from_node(parent, source)
302        && best.as_deref() != Some(recovered.as_str())
303    {
304        return Some(recovered);
305    }
306    best.or_else(|| {
307        node.child_by_field_name("name")
308            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
309            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
310    })
311}
312
313fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
314    let mut grammar_name = None;
315    if let Some(name_node) = node.child_by_field_name("name") {
316        let name = normalize_cpp_whitespace(node_text(name_node, source));
317        if name.is_empty() {
318            return None;
319        }
320        if !cpp_export_macro_token(&name) {
321            return Some(name);
322        }
323        grammar_name = Some(name);
324    }
325
326    let mut best = None;
327    let mut cursor = node.walk();
328    let mut stack = Vec::new();
329    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
330        if matches!(
331            child.kind(),
332            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
333        ) {
334            break;
335        }
336        stack.push(child);
337    }
338
339    while let Some(current) = stack.pop() {
340        if matches!(current.kind(), "type_identifier" | "identifier") {
341            let name = normalize_cpp_whitespace(node_text(current, source));
342            if !name.is_empty() && !cpp_export_macro_token(&name) {
343                best = Some(name);
344            }
345            continue;
346        }
347
348        for index in (0..current.named_child_count()).rev() {
349            if let Some(child) = current.named_child(index) {
350                stack.push(child);
351            }
352        }
353    }
354    best.or(grammar_name)
355}
356
357pub fn cpp_export_macro_token(token: &str) -> bool {
358    token
359        .chars()
360        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
361}
362
363struct RecoveredExportedClass<'tree> {
364    declaration_node: Node<'tree>,
365    name: String,
366    body: Option<Node<'tree>>,
367    raw_supertypes: Option<Vec<String>>,
368    uses_initializer_body: bool,
369    /// Present only for the fragmented multiple-base export shape (issue #938).
370    /// Carries the true class-body byte region -- the members tree-sitter scattered
371    /// out of the recovered node -- so they can be reparsed and re-owned as members
372    /// rather than lost inside the truncated `initializer_list` stand-in.
373    fragmented_body: Option<FragmentedExportBody>,
374}
375
376/// The recovered class-body geometry for a fragmented multiple-base export class.
377/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
378/// verbatim for a region reparse (issue #941 machinery) so every recovered member
379/// keeps its exact original byte/line position. `class_range` is the full class
380/// navigation range spanning to the displaced closing brace.
381struct FragmentedExportBody {
382    reparse_start: usize,
383    reparse_end: usize,
384    class_range: Range,
385}
386
387struct DisplacedFragmentNamespaceBoundary<'tree> {
388    class_close: Node<'tree>,
389    class_semicolon: Node<'tree>,
390    namespace_items: Vec<Node<'tree>>,
391}
392
393/// Result of validating a reparsed fragmented class body.  A complete tree can
394/// safely consume the whole region.  A partial tree may contain only the exact
395/// class-named constructor that tree-sitter merged into an access label; its
396/// remaining siblings must stay on the ordinary outer walk.
397enum FragmentedExportMembers {
398    Complete(Tree),
399    ConditionalConstructor(Tree),
400}
401
402#[derive(Clone, Copy)]
403struct DisplacedMacroClassTail {
404    split_index: usize,
405    class_range: Range,
406}
407
408fn recover_exported_class_declaration<'tree>(
409    node: Node<'tree>,
410    source: &str,
411) -> Option<RecoveredExportedClass<'tree>> {
412    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
413        return Some(recovered);
414    }
415
416    let class_node = first_class_like_child(node)?;
417    if let Some(name_node) = class_node.child_by_field_name("name") {
418        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
419        if cpp_export_macro_token(&class_name) {
420            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
421            // Name declarator. Only a bare declarator can be the displaced class name;
422            // wrappers describe an object whose type merely happens to look macro-like.
423            let mut cursor = node.walk();
424            if node
425                .children_by_field_name("declarator", &mut cursor)
426                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
427            {
428                return None;
429            }
430        } else if has_direct_cpp_declarator(node) {
431            return None;
432        }
433    }
434    let name = exported_class_name_from_node(class_node, source)?;
435    Some(RecoveredExportedClass {
436        declaration_node: class_node,
437        name,
438        body: cpp_body_node(class_node),
439        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
440            .then(|| extract_cpp_supertypes(class_node, source)),
441        uses_initializer_body: false,
442        fragmented_body: None,
443    })
444}
445
446fn recover_malformed_exported_base_class<'tree>(
447    node: Node<'tree>,
448    source: &str,
449) -> Option<RecoveredExportedClass<'tree>> {
450    if node.kind() != "declaration" {
451        return None;
452    }
453    let class_node = node.child_by_field_name("type")?;
454    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
455        return None;
456    }
457    let macro_name = class_node
458        .child_by_field_name("name")
459        .and_then(|name| direct_identifier_name(name, source))?;
460    if !cpp_export_macro_token(&macro_name) {
461        return None;
462    }
463
464    let mut named_cursor = node.walk();
465    let mut named = node.named_children(&mut named_cursor);
466    if named
467        .next()
468        .is_none_or(|child| !same_node(child, class_node))
469    {
470        return None;
471    }
472    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
473    if displaced.kind() != "ERROR" {
474        return None;
475    }
476    let name = displaced_exported_class_name(displaced, source)?;
477
478    let remaining = named.collect::<Vec<_>>();
479    let init = *remaining.last()?;
480    if init.kind() != "init_declarator" {
481        return None;
482    }
483    let final_base = init
484        .child_by_field_name("declarator")
485        .and_then(|base| recovered_malformed_base_name(base, source))?;
486    let body = init.child_by_field_name("value")?;
487    // A complete reduction has a real closing brace here. In Chromium's Widget
488    // declaration, tree-sitter instead emits the same direct `}` slot as a
489    // zero-width missing node where the first body macro truncates the prefix.
490    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
491        return None;
492    }
493
494    if remaining[..remaining.len() - 1]
495        .iter()
496        .any(|child| match child.kind() {
497            "qualified_identifier"
498            | "scoped_type_identifier"
499            | "type_identifier"
500            | "identifier" => false,
501            "ERROR" => !is_malformed_inheritance_access(*child, source),
502            _ => true,
503        })
504    {
505        return None;
506    }
507
508    let mut raw_supertypes = Vec::new();
509    for base in &remaining[..remaining.len() - 1] {
510        if base.kind() == "ERROR" {
511            continue;
512        }
513        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
514    }
515    raw_supertypes.push(final_base);
516
517    Some(RecoveredExportedClass {
518        declaration_node: node,
519        name,
520        body: Some(body),
521        raw_supertypes: Some(raw_supertypes),
522        uses_initializer_body: true,
523        fragmented_body: fragmented_export_body_region(node, body, source),
524    })
525}
526
527/// Locate the true class-body region for a fragmented multiple-base export class.
528///
529/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
530/// emits in place of the real class body. Tree-sitter reduces that body in one of
531/// two shapes, both of which lose the members from the recovered node:
532///
533/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
534///   a real closing brace and holds the whole body text inline. The interior between
535///   the braces reparses to the members directly.
536/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
537///   first member with a zero-width MISSING `}`; every later member -- and the real
538///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
539///   siblings. The interior runs from the opening brace to that displaced `}`.
540///
541/// Returns the interior byte range to reparse plus the full class navigation range.
542fn fragmented_export_body_region(
543    node: Node<'_>,
544    body: Node<'_>,
545    source: &str,
546) -> Option<FragmentedExportBody> {
547    let reparse_start = body.start_byte() + 1;
548    let close = direct_close_brace(body)?;
549    if close.end_byte() > close.start_byte() {
550        return Some(FragmentedExportBody {
551            reparse_start,
552            reparse_end: close.start_byte(),
553            class_range: cpp_declaration_range(node),
554        });
555    }
556    // The closing brace was displaced past the recovered node. A balanced nested
557    // class keeps its own braces, so the first lone-`}` sibling is this class's.
558    let mut sibling = node.next_named_sibling();
559    let displaced_close = loop {
560        let Some(current) = sibling else {
561            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
562        };
563        if cpp_is_stray_close_brace(current, source) {
564            break current;
565        }
566        sibling = current.next_named_sibling();
567    };
568    Some(FragmentedExportBody {
569        reparse_start,
570        reparse_end: displaced_close.start_byte(),
571        class_range: Range {
572            start_byte: node.start_byte(),
573            end_byte: displaced_close.end_byte(),
574            start_line: node.start_position().row + 1,
575            end_line: displaced_close.end_position().row + 1,
576        },
577    })
578}
579
580/// Locate the true class-body region for the export-macro class shape that
581/// tree-sitter promotes to a `function_definition`.
582///
583/// In this shape the synthetic function body closes at the first inline
584/// method, while the class's real members continue as root-level siblings until
585/// a stray `}` followed by the displaced class `;`. Reparse the complete
586/// interior so those siblings are visited with the recovered class scope.
587fn fragmented_export_function_body_region(
588    node: Node<'_>,
589    body: Node<'_>,
590    source: &str,
591    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
592) -> Option<FragmentedExportBody> {
593    let reparse_start = body.start_byte().checked_add(1)?;
594    if let Some(boundary) = displaced_namespace {
595        return Some(FragmentedExportBody {
596            reparse_start,
597            reparse_end: boundary.class_close.start_byte(),
598            class_range: Range {
599                start_byte: node.start_byte(),
600                end_byte: boundary.class_semicolon.end_byte(),
601                start_line: node.start_position().row + 1,
602                end_line: boundary.class_semicolon.end_position().row + 1,
603            },
604        });
605    }
606    let siblings = cpp_following_named_siblings(node, source);
607    let boundary = fragmented_export_sibling_class_boundary(node, source);
608    let boundary_index = boundary.and_then(|boundary| {
609        siblings
610            .iter()
611            .position(|candidate| same_node(*candidate, boundary))
612    });
613    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
614    let mut sibling_index = 0;
615    // A complete recovered class's synthetic wrapper is immediately followed
616    // by its displaced semicolon (comments and a trailing attribute macro --
617    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
618    // may sit between the body and that semicolon). Only scan for a later
619    // stray close when real member siblings intervene; otherwise every earlier
620    // complete class would borrow the next malformed class's close and claim
621    // its members. The trailing-attribute case is the gtest shape: the scan
622    // borrowed a close ~1900 lines later and re-owned a following
623    // `namespace testing { namespace internal {` block as class members,
624    // doubling the package path ("testing::internal::testing::internal") and
625    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
626    // boundary assert (#2297).
627    while let Some(current) = siblings.get(sibling_index).copied() {
628        if current.kind() == "comment" {
629            sibling_index += 1;
630            continue;
631        }
632        if is_trailing_attribute_macro_sibling(current) {
633            sibling_index += 1;
634            continue;
635        }
636        if cpp_is_stray_semicolon(current, source) {
637            return None;
638        }
639        break;
640    }
641    while let Some(current) = siblings.get(sibling_index).copied() {
642        let next = siblings.get(sibling_index + 1).copied();
643        if cpp_is_stray_close_brace(current, source)
644            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
645        {
646            let semicolon = next.expect("checked above");
647            return Some(FragmentedExportBody {
648                reparse_start,
649                reparse_end: current.start_byte(),
650                class_range: Range {
651                    start_byte: node.start_byte(),
652                    end_byte: semicolon.end_byte(),
653                    start_line: node.start_position().row + 1,
654                    end_line: semicolon.end_position().row + 1,
655                },
656            });
657        }
658        // When the final access label keeps the class close in its malformed
659        // declaration body, tree-sitter nests the lone `}` ERROR below the
660        // label instead of exposing it as a direct sibling. Search only the
661        // scattered siblings after the synthetic wrapper. The first such
662        // close is the class terminator because nested class bodies retain
663        // their own balanced class_specifier nodes.
664        if current.start_byte() >= body.end_byte()
665            && let Some(close) = cpp_nested_stray_close_brace(current, source)
666        {
667            return Some(FragmentedExportBody {
668                reparse_start,
669                reparse_end: close.start_byte(),
670                class_range: Range {
671                    start_byte: node.start_byte(),
672                    end_byte: current.end_byte(),
673                    start_line: node.start_position().row + 1,
674                    end_line: current.end_position().row + 1,
675                },
676            });
677        }
678        sibling_index += 1;
679    }
680    boundary.map(|boundary| FragmentedExportBody {
681        reparse_start,
682        reparse_end: boundary.start_byte(),
683        class_range: Range {
684            start_byte: node.start_byte(),
685            end_byte: boundary.start_byte(),
686            start_line: node.start_position().row + 1,
687            end_line: boundary.start_position().row + 1,
688        },
689    })
690}
691
692/// Find a later macro-export class that tree-sitter lifted through an enclosing
693/// preprocessor container. A class that is still a direct sibling can be a
694/// nested member of the current fragmented class, so only a changed parent is
695/// a proven boundary between the two recovered class envelopes.
696fn fragmented_export_sibling_class_boundary<'tree>(
697    node: Node<'tree>,
698    source: &str,
699) -> Option<Node<'tree>> {
700    let node_parent = node.parent()?;
701    cpp_following_named_siblings(node, source)
702        .into_iter()
703        .find(|candidate| {
704            recover_exported_class_function_definition(*candidate, source).is_some()
705                && candidate
706                    .parent()
707                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
708        })
709}
710
711/// A trailing attribute macro after a recovered class's closing brace, spelled
712/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
713/// bare identifier is never a class member (members need a type), so this
714/// sibling can only be the class's own tail (#2297).
715fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
716    if node.kind() != "expression_statement" {
717        return false;
718    }
719    let mut cursor = node.walk();
720    let mut children = node.named_children(&mut cursor);
721    children
722        .next()
723        .is_some_and(|child| child.kind() == "identifier")
724        && children.next().is_none()
725}
726
727/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
728/// export-class wrapper can place the class close inside an access-label node,
729/// so direct-sibling checks alone miss the boundary.  Walk named CST children
730/// only; the helper does not inspect source text beyond the existing structured
731/// stray-brace predicate.
732fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
733    let mut stack = vec![node];
734    while let Some(current) = stack.pop() {
735        if cpp_is_stray_close_brace(current, source) {
736            return Some(current);
737        }
738        let mut cursor = current.walk();
739        stack.extend(current.named_children(&mut cursor));
740    }
741    None
742}
743
744/// Return named siblings that follow `node`, including siblings that tree-sitter
745/// attached to an enclosing container after malformed recovery split the local
746/// declaration list. Stop at the first structurally visible class close so a
747/// later namespace or exported class cannot supply the recovery boundary.
748fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
749    let mut siblings = Vec::new();
750    let mut anchor = node;
751    while let Some(parent) = anchor.parent() {
752        let at_translation_unit = parent.kind() == "translation_unit";
753        let mut sibling = anchor.next_named_sibling();
754        while let Some(current) = sibling {
755            if at_translation_unit
756                && (current.kind() == "namespace_definition"
757                    || (current.kind() == "function_definition"
758                        && first_class_like_child(current).is_some()))
759            {
760                return siblings;
761            }
762            siblings.push(current);
763            if cpp_is_stray_close_brace(current, source) {
764                if let Some(semicolon) = current
765                    .next_named_sibling()
766                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
767                {
768                    siblings.push(semicolon);
769                }
770                return siblings;
771            }
772            if current.start_byte() >= node.end_byte()
773                && matches!(current.kind(), "ERROR" | "labeled_statement")
774                && cpp_nested_stray_close_brace(current, source).is_some()
775            {
776                return siblings;
777            }
778            sibling = current.next_named_sibling();
779        }
780        anchor = parent;
781    }
782    siblings
783}
784
785fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
786    if node.start_byte() >= class_end {
787        return false;
788    }
789    node.end_byte() <= class_end
790        || cpp_nested_stray_close_brace(node, source)
791            .is_some_and(|close| close.start_byte() == class_end)
792}
793
794/// Recover a plain class whose opening prefix is retained in one ERROR node
795/// while one or more nested class closes and the outer close are displaced to
796/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
797/// export-class recovery above. All boundaries come from tree-sitter nodes: the
798/// direct class tokens establish nesting depth and the displaced close nodes
799/// terminate it.
800fn fragmented_plain_class_body<'tree>(
801    node: Node<'tree>,
802    source: &str,
803) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
804    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
805        return Some(recovered);
806    }
807    if node.kind() != "ERROR" {
808        return None;
809    }
810    let mut cursor = node.walk();
811    let children = node.children(&mut cursor).collect::<Vec<_>>();
812    let keyword = children.first()?;
813    if !matches!(keyword.kind(), "class" | "struct" | "union") {
814        return None;
815    }
816    let name_node = children
817        .iter()
818        .copied()
819        .skip(1)
820        .find(|child| child.is_named())?;
821    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
822        return None;
823    }
824    let name = normalize_cpp_whitespace(node_text(name_node, source));
825    if name.is_empty() || cpp_export_macro_token(&name) {
826        return None;
827    }
828    let open_index = children.iter().position(|child| child.kind() == "{")?;
829    let open = children[open_index];
830    let nested_class_opens = children[open_index + 1..]
831        .iter()
832        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
833        .count();
834    let mut closes_remaining = 1 + nested_class_opens;
835    let mut sibling = node.next_named_sibling();
836    while let Some(candidate) = sibling {
837        let next = candidate.next_named_sibling();
838        if cpp_is_stray_close_brace(candidate, source) {
839            closes_remaining -= 1;
840            if closes_remaining == 0 {
841                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
842                if open.end_byte() >= candidate.start_byte() {
843                    return None;
844                }
845                return Some((
846                    node,
847                    name,
848                    FragmentedExportBody {
849                        reparse_start: open.end_byte(),
850                        reparse_end: candidate.start_byte(),
851                        class_range: Range {
852                            start_byte: node.start_byte(),
853                            end_byte: semicolon.end_byte(),
854                            start_line: node.start_position().row + 1,
855                            end_line: semicolon.end_position().row + 1,
856                        },
857                    },
858                ));
859            }
860        }
861        sibling = next;
862    }
863    None
864}
865
866pub(crate) fn recovered_fragmented_plain_class_has_body(
867    node: Node<'_>,
868    source: &str,
869    expected_name: &str,
870    expected_range: &Range,
871) -> bool {
872    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
873        name == expected_name
874            && fragmented.class_range.start_byte == expected_range.start_byte
875            && fragmented.class_range.end_byte == expected_range.end_byte
876    })
877}
878
879/// Recover a plain class whose parser-visible body ends inside a malformed
880/// inline member. Tree-sitter then attaches either the next real member
881/// declarator or the unfinished `else` branch directly to the outer function
882/// definition and leaves the class's actual `};` among later siblings. Those
883/// structured continuations and the close/semicolon siblings establish the
884/// complete body envelope without interpreting source text.
885fn fragmented_plain_class_declaration_body<'tree>(
886    node: Node<'tree>,
887    source: &str,
888) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
889    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
890        return None;
891    }
892    let class_node = node.child_by_field_name("type")?;
893    if !matches!(
894        class_node.kind(),
895        "class_specifier" | "struct_specifier" | "union_specifier"
896    ) {
897        return None;
898    }
899    let name_node = class_node.child_by_field_name("name")?;
900    let name = normalize_cpp_whitespace(node_text(name_node, source));
901    if name.is_empty() || cpp_export_macro_token(&name) {
902        return None;
903    }
904    let body = cpp_body_node(class_node)?;
905    if body.kind() != "field_declaration_list" {
906        return None;
907    }
908    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
909        if declarator.start_byte() < class_node.end_byte() {
910            return None;
911        }
912        let mut cursor = node.walk();
913        node.named_children(&mut cursor).any(|child| {
914            if child.kind() != "ERROR"
915                || child.start_byte() < class_node.end_byte()
916                || child.end_byte() > declarator.start_byte()
917            {
918                return false;
919            }
920            let mut cursor = child.walk();
921            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
922            let Some((return_type, attributes)) = components.split_last() else {
923                return false;
924            };
925            matches!(
926                return_type.kind(),
927                "identifier"
928                    | "type_identifier"
929                    | "primitive_type"
930                    | "decltype"
931                    | "placeholder_type_specifier"
932            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
933                && attributes.iter().all(|attribute| {
934                    matches!(attribute.kind(), "identifier" | "type_identifier")
935                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
936                            *attribute, source,
937                        )))
938                })
939        })
940    } else {
941        let mut cursor = node.walk();
942        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
943        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
944            if same_node(*candidate_class, class_node)
945                && continuation.kind() == "identifier"
946                && node_text(*continuation, source) == "else"
947                && continuation_body.kind() == "compound_statement"
948                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
949                && continuation_body
950                    .child(continuation_body.child_count().saturating_sub(1))
951                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
952    };
953    if !displaced_member {
954        return None;
955    }
956    let open = body
957        .children(&mut body.walk())
958        .find(|child| child.kind() == "{")?;
959    let siblings = cpp_following_named_siblings(node, source);
960    let ordinary_boundary =
961        siblings
962            .iter()
963            .copied()
964            .enumerate()
965            .find_map(|(close_index, close)| {
966                cpp_is_stray_close_brace(close, source)
967                    .then(|| {
968                        siblings
969                            .get(close_index + 1)
970                            .copied()
971                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
972                            .map(|semicolon| (close, semicolon))
973                    })
974                    .flatten()
975            });
976    let (close, semicolon) =
977        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
978            (boundary.class_close, boundary.class_semicolon)
979        } else {
980            ordinary_boundary?
981        };
982    if open.end_byte() >= close.start_byte() {
983        return None;
984    }
985    Some((
986        class_node,
987        name,
988        FragmentedExportBody {
989            reparse_start: open.end_byte(),
990            reparse_end: close.start_byte(),
991            class_range: Range {
992                start_byte: class_node.start_byte(),
993                end_byte: semicolon.end_byte(),
994                start_line: class_node.start_position().row + 1,
995                end_line: semicolon.end_position().row + 1,
996            },
997        },
998    ))
999}
1000
1001fn displaced_export_function_namespace_shape<'tree>(
1002    declaration: Node<'tree>,
1003    source: &str,
1004) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1005    let mut nested = Vec::new();
1006    for index in (0..declaration.named_child_count()).rev() {
1007        nested.push(declaration.named_child(index)?);
1008    }
1009    while let Some(current) = nested.pop() {
1010        // A recovered export class nested in this class can consume the first
1011        // parser-visible namespace close itself. In that shape the existing
1012        // later-class boundary logic already distinguishes the nested and
1013        // namespace-sibling owners; do not mistake the nested close for this
1014        // class's terminator.
1015        if recover_exported_class_function_definition(current, source).is_some() {
1016            return None;
1017        }
1018        for index in (0..current.named_child_count()).rev() {
1019            nested.push(current.named_child(index)?);
1020        }
1021    }
1022    let mut same_envelope_sibling = declaration.next_named_sibling();
1023    while let Some(current) = same_envelope_sibling {
1024        if recover_exported_class_function_definition(current, source).is_some() {
1025            return None;
1026        }
1027        same_envelope_sibling = current.next_named_sibling();
1028    }
1029    let declaration_list = declaration.parent()?;
1030    if declaration_list.kind() != "declaration_list" {
1031        return None;
1032    }
1033    let namespace = declaration_list.parent()?;
1034    if namespace.kind() != "namespace_definition"
1035        || namespace.child_by_field_name("body") != Some(declaration_list)
1036    {
1037        return None;
1038    }
1039    let class_close = direct_close_brace(declaration_list)?;
1040    let trailing_semicolon = namespace.next_named_sibling()?;
1041    if trailing_semicolon.kind() != "expression_statement"
1042        || trailing_semicolon.named_child_count() != 0
1043    {
1044        return None;
1045    }
1046    // A chain of malformed export classes can consume one parser-visible
1047    // namespace close per class. Walk through the enclosing sibling levels so
1048    // the later real namespace close remains the structural boundary; a
1049    // direct next-sibling walk stops at the first collapsed namespace and
1050    // incorrectly makes its intervening items members of this class.
1051    let siblings = cpp_following_named_siblings(namespace, source);
1052    let trailing_index = siblings
1053        .iter()
1054        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1055    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1056        recover_exported_class_function_definition(*candidate, source).is_some()
1057    }) {
1058        // Consecutive recovered classes already have an exact sibling-class
1059        // boundary. Preserve that established path, including nested export
1060        // classes, instead of interpreting the first class close as a
1061        // collapsed namespace boundary.
1062        return None;
1063    }
1064    let mut namespace_items = Vec::new();
1065    let mut nested_fragment_end = 0;
1066    for current in siblings.into_iter().skip(trailing_index + 1) {
1067        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1068        {
1069            return Some(DisplacedFragmentNamespaceBoundary {
1070                class_close,
1071                class_semicolon: trailing_semicolon,
1072                namespace_items,
1073            });
1074        }
1075        if current.start_byte() >= nested_fragment_end
1076            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1077        {
1078            nested_fragment_end = fragmented.class_range.end_byte;
1079        } else if current.start_byte() >= nested_fragment_end
1080            && recover_exported_class_function_definition(current, source).is_some()
1081            && let Some(body) = cpp_body_node(current)
1082            && let Some(fragmented) =
1083                fragmented_export_function_body_region(current, body, source, None)
1084        {
1085            nested_fragment_end = fragmented.class_range.end_byte;
1086        }
1087        namespace_items.push(current);
1088    }
1089    None
1090}
1091
1092fn displaced_fragment_namespace_boundary<'tree>(
1093    declaration: Node<'tree>,
1094    body: Node<'tree>,
1095    source: &str,
1096) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1097    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1098    let reparse_start = body.start_byte() + 1;
1099    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1100    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1101}
1102
1103/// Recover the class/namespace brace geometry for a declaration whose class
1104/// close tree-sitter consumed as the enclosing namespace close. This proof is
1105/// independent of whether every member in the class body can be reparsed: the
1106/// ordinary-tree fallback can still re-own bounded sibling declarations when
1107/// an unknown macro makes the complete body reparse unsafe.
1108fn displaced_fragment_namespace_geometry<'tree>(
1109    declaration: Node<'tree>,
1110    source: &str,
1111) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1112    // A templated class's malformed function wrapper remains beneath the
1113    // template node even though its later members have escaped to the
1114    // enclosing declaration list. Lift only that exact declaration child.
1115    let envelope = declaration
1116        .parent()
1117        .filter(|parent| {
1118            parent.kind() == "template_declaration"
1119                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1120        })
1121        .unwrap_or(declaration);
1122    let declaration_list = envelope.parent()?;
1123    if declaration_list.kind() != "declaration_list" {
1124        return None;
1125    }
1126    let namespace = declaration_list.parent()?;
1127    if namespace.kind() != "namespace_definition"
1128        || namespace.child_by_field_name("body") != Some(declaration_list)
1129    {
1130        return None;
1131    }
1132    let class_close = direct_close_brace(declaration_list)?;
1133    let trailing_semicolon = namespace.next_named_sibling()?;
1134    if trailing_semicolon.kind() != "expression_statement"
1135        || trailing_semicolon.named_child_count() != 0
1136    {
1137        return None;
1138    }
1139    let mut namespace_items = Vec::new();
1140    let mut sibling = trailing_semicolon.next_named_sibling();
1141    let mut nested_fragment_end = 0;
1142    loop {
1143        let current = sibling?;
1144        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1145        {
1146            break;
1147        }
1148        if current.start_byte() >= nested_fragment_end
1149            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1150        {
1151            nested_fragment_end = fragmented.class_range.end_byte;
1152        }
1153        namespace_items.push(current);
1154        sibling = current.next_named_sibling();
1155    }
1156    Some(DisplacedFragmentNamespaceBoundary {
1157        class_close,
1158        class_semicolon: trailing_semicolon,
1159        namespace_items,
1160    })
1161}
1162
1163/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1164fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1165    (0..node.child_count())
1166        .filter_map(|index| node.child(index))
1167        .find(|child| !child.is_named() && child.kind() == "}")
1168}
1169
1170/// A displaced lone closing brace: the class close that the fragmented multiple-base
1171/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1172fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1173    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1174}
1175
1176/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1177/// text while skipping line/block comments and string/char literals. The
1178/// exported-class recovery needs this when tree-sitter's bogus
1179/// `function_definition` body runs past the class's true closing brace and
1180/// swallows following siblings (issue #1524): the grammar tree carries no
1181/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1182/// close is located textually. Returns `None` when the text is unbalanced or
1183/// contains a construct the scanner deliberately does not interpret (raw
1184/// strings) -- callers treat that as "cannot partition" and keep the
1185/// un-split recovery.
1186fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1187    let bytes = source.as_bytes();
1188    if bytes.get(open_byte) != Some(&b'{') {
1189        return None;
1190    }
1191    let mut depth = 0usize;
1192    let mut i = open_byte;
1193    while i < bytes.len() {
1194        match bytes[i] {
1195            b'{' => depth += 1,
1196            b'}' => {
1197                depth = depth.checked_sub(1)?;
1198                if depth == 0 {
1199                    return Some(i);
1200                }
1201            }
1202            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1203                while i < bytes.len() && bytes[i] != b'\n' {
1204                    i += 1;
1205                }
1206                continue;
1207            }
1208            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1209                i += 2;
1210                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1211                    i += 1;
1212                }
1213                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1214                continue;
1215            }
1216            quote @ (b'"' | b'\'') => {
1217                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1218                // bail out rather than mis-count.
1219                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1220                    return None;
1221                }
1222                i += 1;
1223                while i < bytes.len() && bytes[i] != quote {
1224                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1225                }
1226                if i >= bytes.len() {
1227                    return None;
1228                }
1229            }
1230            _ => {}
1231        }
1232        i += 1;
1233    }
1234    None
1235}
1236
1237fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1238    let mut name = None;
1239    let mut colon_count = 0;
1240    let mut access_count = 0;
1241    for index in 0..node.child_count() {
1242        let child = node.child(index)?;
1243        match child.kind() {
1244            "identifier" | "type_identifier" if child.is_named() => {
1245                if name.is_some() {
1246                    return None;
1247                }
1248                let candidate = normalize_cpp_whitespace(node_text(child, source));
1249                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1250                    return None;
1251                }
1252                name = Some(candidate);
1253            }
1254            "template_function" | "template_type" if child.is_named() => {
1255                if name.is_some() {
1256                    return None;
1257                }
1258                let candidate = child
1259                    .child_by_field_name("name")
1260                    .and_then(|name| direct_identifier_name(name, source))?;
1261                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1262                    return None;
1263                }
1264                name = Some(candidate);
1265            }
1266            ":" if !child.is_named() => colon_count += 1,
1267            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1268            _ => return None,
1269        }
1270    }
1271    (colon_count == 1 && access_count == 1)
1272        .then_some(name)
1273        .flatten()
1274}
1275
1276fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1277    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1278        return false;
1279    }
1280    node.named_child(0)
1281        .and_then(|child| direct_identifier_name(child, source))
1282        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1283}
1284
1285fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1286    (0..node.child_count()).any(|index| {
1287        node.child(index)
1288            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1289    })
1290}
1291
1292fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1293    match node.kind() {
1294        "type_identifier" | "identifier" | "namespace_identifier" => {
1295            recovered_base_atom(node, source)
1296        }
1297        "template_type" | "template_function" => node
1298            .child_by_field_name("name")
1299            .and_then(|name| recovered_malformed_base_name(name, source)),
1300        "ERROR" => None,
1301        "qualified_identifier" | "scoped_type_identifier" => {
1302            let suffix = node
1303                .child_by_field_name("name")
1304                .and_then(|name| recovered_malformed_base_name(name, source))?;
1305            let scope = node
1306                .child_by_field_name("scope")
1307                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1308            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1309                malformed_qualified_prefix(node, source)?
1310            } else {
1311                if malformed_qualified_prefix(node, source).is_some() {
1312                    return None;
1313                }
1314                scope
1315            };
1316            Some(format!("{prefix}::{suffix}"))
1317        }
1318        _ => None,
1319    }
1320}
1321
1322fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1323    if !matches!(
1324        node.kind(),
1325        "identifier" | "type_identifier" | "namespace_identifier"
1326    ) {
1327        return None;
1328    }
1329    let name = normalize_cpp_whitespace(node_text(node, source));
1330    (!name.is_empty()).then_some(name)
1331}
1332
1333fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1334    let mut prefix = None;
1335    let mut cursor = node.walk();
1336    for error in node
1337        .named_children(&mut cursor)
1338        .filter(|child| child.kind() == "ERROR")
1339    {
1340        if error.named_child_count() != 1 || prefix.is_some() {
1341            return None;
1342        }
1343        prefix = error
1344            .named_child(0)
1345            .and_then(|child| recovered_base_atom(child, source));
1346        prefix.as_ref()?;
1347    }
1348    prefix
1349}
1350
1351fn recover_exported_class_function_definition<'tree>(
1352    node: Node<'tree>,
1353    source: &str,
1354) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1355    if node.kind() != "function_definition" {
1356        return None;
1357    }
1358    let type_node = node.child_by_field_name("type")?;
1359    let declarator = node.child_by_field_name("declarator")?;
1360
1361    if matches!(
1362        type_node.kind(),
1363        "class_specifier" | "struct_specifier" | "union_specifier"
1364    ) {
1365        let type_name = type_node
1366            .child_by_field_name("name")
1367            .and_then(|name| direct_identifier_name(name, source));
1368        let exported_macro_type = type_name
1369            .as_ref()
1370            .is_some_and(|name| cpp_export_macro_token(name));
1371        if exported_macro_type {
1372            let mut cursor = node.walk();
1373            let errors_before_declarator = node
1374                .named_children(&mut cursor)
1375                .filter(|child| {
1376                    child.kind() == "ERROR"
1377                        && child.start_byte() >= type_node.end_byte()
1378                        && child.end_byte() <= declarator.start_byte()
1379                })
1380                .collect::<Vec<_>>();
1381            if let Some(name) = errors_before_declarator
1382                .iter()
1383                .find_map(|error| displaced_exported_class_name(*error, source))
1384            {
1385                let raw_supertypes = errors_before_declarator
1386                    .iter()
1387                    .any(|error| malformed_inheritance_syntax(*error))
1388                    .then(|| recovered_malformed_base_name(declarator, source))
1389                    .flatten()
1390                    .map(|base| vec![base]);
1391                return Some((node, name, raw_supertypes));
1392            }
1393            if errors_before_declarator
1394                .iter()
1395                .any(|error| malformed_inheritance_syntax(*error))
1396            {
1397                return None;
1398            }
1399        }
1400        if !exported_macro_type
1401            && let Some(name) = type_name
1402            && !cpp_export_macro_token(&name)
1403            && let Some(base) =
1404                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1405        {
1406            return Some((node, name, Some(vec![base])));
1407        }
1408        if let Some(name) = direct_identifier_name(declarator, source)
1409            && exported_macro_type
1410            && !cpp_export_macro_token(&name)
1411        {
1412            let raw_supertypes = exported_macro_type
1413                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1414                .flatten()
1415                .map(|base| vec![base]);
1416            return Some((node, name, raw_supertypes));
1417        }
1418        if declarator.kind() == "parenthesized_declarator"
1419            && type_node
1420                .child_by_field_name("name")
1421                .and_then(|name| direct_identifier_name(name, source))
1422                .is_some_and(|name| cpp_export_macro_token(&name))
1423        {
1424            let body_start = node
1425                .child_by_field_name("body")
1426                .map(|body| body.start_byte())
1427                .unwrap_or(node.end_byte());
1428            let mut cursor = node.walk();
1429            if let Some(name) = node
1430                .named_children(&mut cursor)
1431                .filter(|child| {
1432                    child.kind() == "ERROR"
1433                        && child.start_byte() >= declarator.end_byte()
1434                        && child.end_byte() <= body_start
1435                })
1436                .find_map(|error| declarator_name_from_node(error, source))
1437            {
1438                return Some((node, name, None));
1439            }
1440        }
1441    }
1442
1443    let declarator_text = direct_identifier_name(declarator, source)?;
1444    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1445        return None;
1446    }
1447    class_identifier_before_body(node, source).map(|name| (node, name, None))
1448}
1449
1450/// Whether `node` is the base type displaced into the declarator field of an
1451/// export-macro class that tree-sitter represented as a declaration or
1452/// function definition.
1453///
1454/// Declaration extraction already recovers this exact malformed envelope as a
1455/// class and records the declarator as its base. Reference extraction must use
1456/// the same structural fact instead of treating the node as a function name.
1457pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1458    if !matches!(
1459        node.kind(),
1460        "qualified_identifier" | "scoped_type_identifier" | "template_type"
1461    ) {
1462        return false;
1463    }
1464    if let Some(function) = node.parent().filter(|parent| {
1465        parent.kind() == "function_definition"
1466            && parent
1467                .child_by_field_name("declarator")
1468                .is_some_and(|declarator| same_node(declarator, node))
1469    }) {
1470        return recover_exported_class_function_definition(function, source)
1471            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1472    }
1473    let Some(initializer) = node.parent().filter(|parent| {
1474        parent.kind() == "init_declarator"
1475            && parent
1476                .child_by_field_name("declarator")
1477                .is_some_and(|declarator| same_node(declarator, node))
1478    }) else {
1479        return false;
1480    };
1481    initializer
1482        .parent()
1483        .filter(|parent| parent.kind() == "declaration")
1484        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1485        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1486}
1487
1488/// Recover the class item from a region reparse that still carries the
1489/// sentinel's synthetic function envelope.  An unknown class attribute can
1490/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1491/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1492/// then nested below that function, so direct class-child lookup is not enough.
1493struct CppSentinelReparsedClass<'tree> {
1494    declaration_node: Node<'tree>,
1495    name: String,
1496    body: Node<'tree>,
1497    raw_supertypes: Option<Vec<String>>,
1498}
1499
1500fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1501    let mut cursor = root.walk();
1502    root.named_children(&mut cursor)
1503        .find(|child| child.kind() != "comment")
1504        .filter(|child| child.kind() == "template_declaration")
1505}
1506
1507fn cpp_sentinel_reparsed_class<'tree>(
1508    root: Node<'tree>,
1509    template_node: Option<Node<'tree>>,
1510    source: &str,
1511) -> Option<CppSentinelReparsedClass<'tree>> {
1512    let container = template_node.unwrap_or(root);
1513    let mut cursor = container.walk();
1514    for child in container.named_children(&mut cursor) {
1515        if matches!(
1516            child.kind(),
1517            "class_specifier" | "struct_specifier" | "union_specifier"
1518        ) {
1519            let name = class_like_name(child, source)?;
1520            let body = cpp_body_node(child)?;
1521            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1522                .then(|| extract_cpp_supertypes(child, source));
1523            return Some(CppSentinelReparsedClass {
1524                declaration_node: child,
1525                name,
1526                body,
1527                raw_supertypes,
1528            });
1529        }
1530        if child.kind() == "declaration"
1531            && let Some(class_node) = first_class_like_child(child)
1532        {
1533            let name = class_like_name(class_node, source)?;
1534            let body = cpp_body_node(class_node)?;
1535            let raw_supertypes =
1536                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1537                    .then(|| extract_cpp_supertypes(class_node, source));
1538            return Some(CppSentinelReparsedClass {
1539                declaration_node: class_node,
1540                name,
1541                body,
1542                raw_supertypes,
1543            });
1544        }
1545        // Only when the nested class item carries its own body. A bodyless
1546        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1547        // a function definition -- is the export-macro shape recovered by the
1548        // next arm, and must fall through to it rather than abort the search.
1549        if child.kind() == "function_definition"
1550            && let Some(class_node) = first_class_like_child(child)
1551            && let Some(body) = cpp_body_node(class_node)
1552            && let Some(name) = class_like_name(class_node, source)
1553        {
1554            let raw_supertypes =
1555                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1556                    .then(|| extract_cpp_supertypes(class_node, source));
1557            return Some(CppSentinelReparsedClass {
1558                declaration_node: class_node,
1559                name,
1560                body,
1561                raw_supertypes,
1562            });
1563        }
1564        if child.kind() == "function_definition"
1565            && let Some((_, name, raw_supertypes)) =
1566                recover_exported_class_function_definition(child, source)
1567        {
1568            let body = cpp_body_node(child)?;
1569            return Some(CppSentinelReparsedClass {
1570                declaration_node: child,
1571                name,
1572                body,
1573                raw_supertypes,
1574            });
1575        }
1576    }
1577    None
1578}
1579
1580fn recovered_postfix_export_macro_base(
1581    node: Node<'_>,
1582    type_node: Node<'_>,
1583    declarator: Node<'_>,
1584    source: &str,
1585) -> Option<String> {
1586    let mut cursor = node.walk();
1587    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1588        child.kind() == "ERROR"
1589            && child.start_byte() >= type_node.end_byte()
1590            && child.end_byte() <= declarator.start_byte()
1591            && postfix_export_macro_inheritance(*child, source)
1592    });
1593    malformed_clauses.next()?;
1594    if malformed_clauses.next().is_some() {
1595        return None;
1596    }
1597    recovered_malformed_base_name(declarator, source)
1598}
1599
1600fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1601    let mut macro_count = 0;
1602    let mut colon_count = 0;
1603    let mut access_count = 0;
1604    for index in 0..node.child_count() {
1605        let Some(child) = node.child(index) else {
1606            return false;
1607        };
1608        match child.kind() {
1609            "identifier" | "type_identifier" if child.is_named() => {
1610                let candidate = normalize_cpp_whitespace(node_text(child, source));
1611                if !cpp_export_macro_token(&candidate) {
1612                    return false;
1613                }
1614                macro_count += 1;
1615            }
1616            ":" if !child.is_named() => colon_count += 1,
1617            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1618            _ => return false,
1619        }
1620    }
1621    macro_count == 1 && colon_count == 1 && access_count == 1
1622}
1623
1624fn recovered_single_base_after_declarator(
1625    node: Node<'_>,
1626    declarator: Node<'_>,
1627    source: &str,
1628) -> Option<String> {
1629    let body_start = node
1630        .child_by_field_name("body")
1631        .map(|body| body.start_byte())
1632        .unwrap_or(node.end_byte());
1633    let mut cursor = node.walk();
1634    let mut bases = node
1635        .named_children(&mut cursor)
1636        .filter(|child| {
1637            child.kind() == "ERROR"
1638                && child.start_byte() >= declarator.end_byte()
1639                && child.end_byte() <= body_start
1640        })
1641        .filter_map(|error| displaced_exported_class_name(error, source));
1642    let base = bases.next()?;
1643    bases.next().is_none().then_some(base)
1644}
1645
1646fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1647    (0..node.child_count()).any(|index| {
1648        node.child(index)
1649            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1650    })
1651}
1652
1653pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1654    recover_exported_class_function_definition(node, source).is_some()
1655}
1656
1657fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1658    matches!(
1659        kind,
1660        "ERROR"
1661            | "preproc_if"
1662            | "preproc_ifdef"
1663            | "preproc_ifndef"
1664            | "preproc_else"
1665            | "preproc_elif"
1666    ) || (kind == "labeled_statement" && in_class_scope)
1667}
1668
1669pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1670    if node.kind() != "declaration" {
1671        return false;
1672    }
1673    let mut ancestor = node.parent();
1674    while let Some(container) = ancestor {
1675        match container.kind() {
1676            "compound_statement" => {
1677                return container.parent().is_some_and(|class_container| {
1678                    is_recovered_exported_class_container(class_container, source)
1679                });
1680            }
1681            // These containers preserve ScopeInfo in visit_node. declaration_list is
1682            // the body container selected for a linkage specification.
1683            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1684            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1685            _ => return false,
1686        }
1687        ancestor = container.parent();
1688    }
1689    false
1690}
1691
1692pub fn recovered_exported_class_has_body(
1693    node: Node<'_>,
1694    source: &str,
1695    expected_name: &str,
1696) -> Option<bool> {
1697    match node.kind() {
1698        "function_definition" => {
1699            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1700            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1701        }
1702        "declaration" | "field_declaration" => {
1703            let recovered = recover_exported_class_declaration(node, source)?;
1704            (recovered.name == expected_name).then(|| recovered.body.is_some())
1705        }
1706        _ => None,
1707    }
1708}
1709
1710fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1711    let body_start = node
1712        .child_by_field_name("body")
1713        .map(|body| body.start_byte())
1714        .unwrap_or(node.end_byte());
1715    let mut stack = Vec::new();
1716    for index in (0..node.named_child_count()).rev() {
1717        let Some(child) = node.named_child(index) else {
1718            continue;
1719        };
1720        if child.start_byte() >= body_start {
1721            continue;
1722        }
1723        stack.push(child);
1724    }
1725
1726    let mut best = None;
1727    while let Some(current) = stack.pop() {
1728        if matches!(current.kind(), "identifier" | "type_identifier") {
1729            let name = normalize_cpp_whitespace(node_text(current, source));
1730            if !name.is_empty()
1731                && !cpp_export_macro_token(&name)
1732                && !matches!(name.as_str(), "class" | "struct" | "union")
1733            {
1734                best = Some(name);
1735            }
1736            continue;
1737        }
1738
1739        for index in (0..current.named_child_count()).rev() {
1740            if let Some(child) = current.named_child(index)
1741                && child.start_byte() < body_start
1742            {
1743                stack.push(child);
1744            }
1745        }
1746    }
1747    best
1748}
1749
1750fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1751    if node.kind() == "declaration"
1752        && node
1753            .child_by_field_name("type")
1754            .or_else(|| first_class_like_child(node))
1755            .is_some_and(|type_node| {
1756                matches!(
1757                    type_node.kind(),
1758                    "class_specifier" | "struct_specifier" | "union_specifier"
1759                )
1760            })
1761        && let Some(name) = node
1762            .child_by_field_name("declarator")
1763            .and_then(|declarator| declarator_name_from_node(declarator, source))
1764        && !cpp_export_macro_token(&name)
1765    {
1766        return Some(name);
1767    }
1768
1769    if node.kind() == "function_definition"
1770        && node.child_by_field_name("type").is_some_and(|type_node| {
1771            matches!(
1772                type_node.kind(),
1773                "class_specifier" | "struct_specifier" | "union_specifier"
1774            )
1775        })
1776        && let Some(name) = node
1777            .child_by_field_name("declarator")
1778            .and_then(|declarator| direct_identifier_name(declarator, source))
1779        && !cpp_export_macro_token(&name)
1780    {
1781        return Some(name);
1782    }
1783
1784    let class_node = if matches!(
1785        node.kind(),
1786        "class_specifier" | "struct_specifier" | "union_specifier"
1787    ) {
1788        node
1789    } else {
1790        first_class_like_child(node)?
1791    };
1792    class_like_name_from_children(class_node, source)
1793}
1794
1795fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1796    if !matches!(
1797        node.kind(),
1798        "identifier" | "field_identifier" | "type_identifier"
1799    ) {
1800        return None;
1801    }
1802    let name = normalize_cpp_whitespace(node_text(node, source));
1803    (!name.is_empty()).then_some(name)
1804}
1805
1806fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1807    match node.kind() {
1808        "identifier" | "field_identifier" | "type_identifier" => {
1809            let name = normalize_cpp_whitespace(node_text(node, source));
1810            (!name.is_empty()).then_some(name)
1811        }
1812        _ => {
1813            let mut cursor = node.walk();
1814            node.named_children(&mut cursor)
1815                .find_map(|child| declarator_name_from_node(child, source))
1816        }
1817    }
1818}
1819
1820fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1821    let mut cursor = node.walk();
1822    node.named_children(&mut cursor).find(|child| {
1823        matches!(
1824            child.kind(),
1825            "class_specifier" | "struct_specifier" | "union_specifier"
1826        )
1827    })
1828}
1829
1830/// Push a container's children as a `Siblings` cursor rather than snapshotting
1831/// them all with one shared scope: children are visited one at a time so a
1832/// `using namespace X;` sibling can affect the scope threaded to the siblings
1833/// that textually follow it (issue #1093).
1834fn push_cpp_container_work<'tree>(
1835    node: Node<'tree>,
1836    scope: ScopeInfo,
1837    stack: &mut Vec<CppWork<'tree>>,
1838) {
1839    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1840}
1841
1842/// Materialize one selected named-child range with a tree-sitter cursor. The
1843/// cursor advances linearly across the parent's concrete children; repeatedly
1844/// asking for `named_child(index)` is quadratic on very wide generated nodes.
1845fn push_cpp_sibling_range<'tree>(
1846    parent: Node<'tree>,
1847    start_index: usize,
1848    end_index: usize,
1849    scope: ScopeInfo,
1850    stack: &mut Vec<CppWork<'tree>>,
1851) {
1852    let mut cursor = parent.walk();
1853    let children = parent
1854        .named_children(&mut cursor)
1855        .skip(start_index)
1856        .take(end_index.saturating_sub(start_index))
1857        .collect::<Vec<_>>()
1858        .into_iter();
1859    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1860}
1861
1862/// Advance a `Siblings` cursor by one child: dispatch the current child under
1863/// the scope accumulated from its *earlier* siblings, then push a
1864/// continuation for the remaining siblings carrying the scope updated for
1865/// *this* child (only `using namespace X;` directives change it). Pushing the
1866/// continuation before the current child's own node work means the current
1867/// child's subtree fully drains (LIFO) before the next sibling is visited,
1868/// preserving left-to-right order.
1869fn advance_cpp_siblings<'tree>(
1870    mut siblings: CppSiblingsWork<'tree>,
1871    source: &str,
1872    stack: &mut Vec<CppWork<'tree>>,
1873) {
1874    let Some(child) = siblings.children.next() else {
1875        return;
1876    };
1877    let current_scope = siblings.scope.clone();
1878    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1879        siblings.scope.visible_using_namespaces.push(namespace);
1880    }
1881    if !siblings.children.as_slice().is_empty() {
1882        stack.push(CppWork::Siblings(siblings));
1883    }
1884    stack.push(CppWork::Node(CppNodeWork {
1885        node: child,
1886        scope: current_scope,
1887    }));
1888}
1889
1890/// The namespace target of a `using namespace X;` directive, or `None` for
1891/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1892/// kind. Distinguished structurally by the presence of the grammar's literal
1893/// `namespace` keyword token among the node's children -- not by inspecting
1894/// source text -- so it never misreads a member-importing using-declaration
1895/// as a namespace directive.
1896fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1897    if node.kind() != "using_declaration" {
1898        return None;
1899    }
1900    let mut cursor = node.walk();
1901    let is_namespace_directive = node
1902        .children(&mut cursor)
1903        .any(|child| child.kind() == "namespace");
1904    if !is_namespace_directive {
1905        return None;
1906    }
1907    let target = node.named_child(0)?;
1908    // A leading `::` is the explicit-global marker, not part of the namespace
1909    // path (`using namespace ::std::chrono;`). Drop that AST token before
1910    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
1911    // keeps: storing the marker verbatim desynced the legacy package string from
1912    // the FqName bridge, which splits on `::` and drops the empty leading
1913    // component, tripping the package/short boundary assert when a bare-owner
1914    // out-of-line definition borrowed the directive's namespace (#1093 path).
1915    let start = target
1916        .child(0)
1917        .filter(|child| !child.is_named() && child.kind() == "::")
1918        .map_or(target.start_byte(), |marker| marker.end_byte());
1919    let text = normalize_cpp_whitespace(
1920        source
1921            .get(start..target.end_byte())
1922            .expect("using-directive target covers one source range"),
1923    );
1924    (!text.is_empty()).then_some(text)
1925}
1926
1927/// Every `using namespace X;` directive target in a file, in source order, for
1928/// resolution-time consumers that need the file's using-directives without the
1929/// per-position scope threading extraction does. Parses `source` fresh and
1930/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1931/// keys on the grammar's `namespace` keyword token, not source text), so it
1932/// never misreads a member-importing `using X::Y;` as a namespace directive.
1933///
1934/// This is a whole-file over-approximation of what is in scope at any one point
1935/// (a directive nested inside a `namespace {}` block or a function body is still
1936/// reported), which is exactly what the #1134 identity reconciler wants: extra
1937/// candidate namespaces that no visible class confirms are harmless, and two
1938/// that both confirm are treated as a genuine ambiguity by the reconciler.
1939pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1940    let mut parser = Parser::new();
1941    if parser
1942        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1943        .is_err()
1944    {
1945        return Vec::new();
1946    }
1947    let Some(tree) = parser.parse(source, None) else {
1948        return Vec::new();
1949    };
1950    let mut namespaces = Vec::new();
1951    let mut seen = std::collections::HashSet::new();
1952    let mut stack = vec![tree.root_node()];
1953    while let Some(node) = stack.pop() {
1954        if let Some(namespace) = cpp_using_namespace_target(node, source)
1955            && seen.insert(namespace.clone())
1956        {
1957            namespaces.push(namespace);
1958        }
1959        let mut cursor = node.walk();
1960        stack.extend(node.named_children(&mut cursor));
1961    }
1962    namespaces
1963}
1964
1965pub struct CppVisitor<'a> {
1966    pub file: &'a ProjectFile,
1967    pub source: &'a str,
1968    pub parsed: &'a mut ParsedFile,
1969    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
1970    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
1971    ///
1972    /// C has no nested tag scope: a struct/union/enum tag declared inside
1973    /// another aggregate's member list has the scope of the outer declaration
1974    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
1975    /// therefore declares a file-scope `inner` that a later file-scope
1976    /// `struct inner *p;` legitimately references, where C++ would make the
1977    /// same shape a nested class `outer::inner`. Headers carry no compilation
1978    /// language of their own and keep the conservative C++ interpretation.
1979    pub c_tag_semantics: bool,
1980    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
1981    /// Byte regions whose contents were re-owned by a fragmented export-class
1982    /// recovery (#938): the scattered members between the fragmented
1983    /// declaration and its displaced closing brace are indexed as members of
1984    /// the recovered class by the region reparse, so the ordinary sibling walk
1985    /// must not ALSO index them as top-level declarations (that double-indexing
1986    /// made a scattered nested class ambiguous between `Inner` and
1987    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
1988    /// linear scan at visit time is fine.
1989    pub consumed_fragment_regions: Vec<(usize, usize)>,
1990}
1991
1992impl<'a> CppVisitor<'a> {
1993    #[allow(clippy::too_many_arguments)]
1994    pub fn visit_container(
1995        &mut self,
1996        node: Node<'_>,
1997        package_name: &str,
1998        module: Option<CodeUnit>,
1999        class_unit: Option<CodeUnit>,
2000        template_signature: Option<String>,
2001        visible_using_namespaces: Vec<String>,
2002    ) {
2003        let scope = ScopeInfo {
2004            package_name: package_name.to_string(),
2005            module,
2006            class_unit,
2007            template_signature,
2008            template_metadata: None,
2009            declarations_are_fields: false,
2010            recovered_specialization_member_scope: false,
2011            visible_using_namespaces,
2012        };
2013        self.run_container_work(node, scope);
2014    }
2015
2016    /// Whether a work node lies entirely inside a byte region consumed by a
2017    /// fragmented export-class recovery (#938); such nodes were already indexed
2018    /// as members of the recovered class by the region reparse.
2019    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2020        self.consumed_fragment_regions
2021            .iter()
2022            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2023    }
2024
2025    /// Drive the container work loop from an explicit seed scope to completion. The
2026    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
2027    /// stays alive for the whole traversal.
2028    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2029        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2030        while let Some(work) = stack.pop() {
2031            match work {
2032                CppWork::Container(container) => {
2033                    push_cpp_container_work(container.node, container.scope, &mut stack);
2034                }
2035                CppWork::Siblings(siblings) => {
2036                    advance_cpp_siblings(siblings, self.source, &mut stack);
2037                }
2038                CppWork::Node(work) => {
2039                    if self.node_is_inside_consumed_fragment(work.node) {
2040                        continue;
2041                    }
2042                    self.visit_node(work.node, &work.scope, &mut stack);
2043                }
2044            }
2045        }
2046    }
2047
2048    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
2049    /// it only when the entire region is member-shaped. This validation must happen
2050    /// before registering the recovered class because a rejected speculative range
2051    /// must not leak into the ordinary recovery path.
2052    fn reparse_fragmented_export_class_members(
2053        &self,
2054        fragmented: &FragmentedExportBody,
2055        class_name: &str,
2056    ) -> Option<FragmentedExportMembers> {
2057        if fragmented.reparse_start >= fragmented.reparse_end {
2058            return None;
2059        }
2060        let tree = cpp_reparse_fragmented_class_body(
2061            self.source,
2062            fragmented.reparse_start,
2063            fragmented.reparse_end,
2064        )?;
2065        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2066            return Some(FragmentedExportMembers::Complete(tree));
2067        }
2068        let has_conditional_constructor = {
2069            let root = tree.root_node();
2070            let mut cursor = root.walk();
2071            root.named_children(&mut cursor).any(|child| {
2072                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2073            })
2074        };
2075        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2076    }
2077
2078    /// Index an already validated fragmented body as members of `class_unit`. The
2079    /// region reparse keeps each member's exact original byte and line positions.
2080    fn visit_fragmented_export_class_members(
2081        &mut self,
2082        outcome: FragmentedExportMembers,
2083        class_unit: CodeUnit,
2084        scope: &ScopeInfo,
2085    ) -> bool {
2086        let (tree, complete) = match outcome {
2087            FragmentedExportMembers::Complete(tree) => (tree, true),
2088            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2089        };
2090        let root = tree.root_node();
2091        let class_name = class_unit.identifier().to_string();
2092        let member_scope = ScopeInfo {
2093            // A recovered export-macro class may borrow its namespace from an
2094            // earlier forward declaration even when the malformed node itself
2095            // sits at file scope. Use the recovered class identity as the
2096            // authoritative package for reparsed members as well.
2097            package_name: class_unit.package_name().to_string(),
2098            module: scope.module.clone(),
2099            class_unit: Some(class_unit),
2100            template_signature: scope.template_signature.clone(),
2101            template_metadata: None,
2102            declarations_are_fields: true,
2103            recovered_specialization_member_scope: false,
2104            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2105        };
2106        if !complete {
2107            // A conditional beginning immediately after an access label can
2108            // fragment one constructor declaration while leaving the rest of
2109            // the class body as unsafe statement soup. Recover only that
2110            // structurally proven constructor and leave the outer-tree
2111            // siblings unconsumed for their ordinary walk.
2112            let mut cursor = root.walk();
2113            let constructors = root
2114                .named_children(&mut cursor)
2115                .filter_map(|child| {
2116                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2117                })
2118                .collect::<Vec<_>>();
2119            for constructor in constructors {
2120                let mut stack = Vec::new();
2121                self.visit_node(constructor, &member_scope, &mut stack);
2122                while let Some(work) = stack.pop() {
2123                    match work {
2124                        CppWork::Container(container) => {
2125                            push_cpp_container_work(container.node, container.scope, &mut stack);
2126                        }
2127                        CppWork::Siblings(siblings) => {
2128                            advance_cpp_siblings(siblings, self.source, &mut stack);
2129                        }
2130                        CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
2131                    }
2132                }
2133            }
2134            return false;
2135        }
2136        self.run_container_work(root, member_scope);
2137        true
2138    }
2139
2140    fn visit_recovered_fragment_constructor(
2141        &mut self,
2142        range: std::ops::Range<usize>,
2143        constructor_body: Node<'_>,
2144        class_declaration: Node<'_>,
2145        class_unit: &CodeUnit,
2146        scope: &ScopeInfo,
2147    ) {
2148        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2149            return;
2150        };
2151        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2152            tree.root_node(),
2153            range.start,
2154            class_unit.identifier(),
2155            self.source,
2156        ) else {
2157            return;
2158        };
2159        let member_scope = ScopeInfo {
2160            package_name: class_unit.package_name().to_string(),
2161            module: scope.module.clone(),
2162            class_unit: Some(class_unit.clone()),
2163            template_signature: scope.template_signature.clone(),
2164            template_metadata: None,
2165            declarations_are_fields: true,
2166            recovered_specialization_member_scope: false,
2167            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2168        };
2169        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2170        else {
2171            return;
2172        };
2173        debug_assert_eq!(function.name, class_unit.identifier());
2174        let code_unit = function.code_unit(self.file.clone());
2175        self.parsed.add_code_unit_with_range(
2176            code_unit.clone(),
2177            Range {
2178                start_byte: function_declarator.start_byte(),
2179                end_byte: constructor_body.end_byte(),
2180                start_line: function_declarator.start_position().row + 1,
2181                end_line: constructor_body.end_position().row + 1,
2182            },
2183            None,
2184            None,
2185        );
2186        self.parsed.add_signature_with_metadata(
2187            code_unit.clone(),
2188            cpp_signature_metadata(
2189                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2190                function_declarator,
2191                self.source,
2192            )
2193            .with_declaration_only(false)
2194            .with_callable_linkage(cpp_callable_linkage(class_declaration, self.source)),
2195        );
2196        self.parsed.add_child(class_unit.clone(), code_unit);
2197    }
2198
2199    fn visit_recovered_fragment_prefix_members(
2200        &mut self,
2201        root: Node<'_>,
2202        constructor_start: usize,
2203        class_unit: &CodeUnit,
2204        scope: &ScopeInfo,
2205    ) {
2206        let member_scope = ScopeInfo {
2207            package_name: class_unit.package_name().to_string(),
2208            module: scope.module.clone(),
2209            class_unit: Some(class_unit.clone()),
2210            template_signature: scope.template_signature.clone(),
2211            template_metadata: None,
2212            declarations_are_fields: true,
2213            recovered_specialization_member_scope: false,
2214            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2215        };
2216        let mut stack = vec![root];
2217        while let Some(current) = stack.pop() {
2218            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2219                continue;
2220            }
2221            if current.end_byte() <= constructor_start
2222                && current.kind() != "translation_unit"
2223                && current.kind() != "labeled_statement"
2224                && current.kind() != "ERROR"
2225            {
2226                let mut work_stack = Vec::new();
2227                self.visit_node(current, &member_scope, &mut work_stack);
2228                while let Some(work) = work_stack.pop() {
2229                    match work {
2230                        CppWork::Container(container) => {
2231                            push_cpp_container_work(
2232                                container.node,
2233                                container.scope,
2234                                &mut work_stack,
2235                            );
2236                        }
2237                        CppWork::Siblings(siblings) => {
2238                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
2239                        }
2240                        CppWork::Node(work) => {
2241                            self.visit_node(work.node, &work.scope, &mut work_stack)
2242                        }
2243                    }
2244                }
2245                continue;
2246            }
2247            if matches!(
2248                current.kind(),
2249                "translation_unit" | "labeled_statement" | "ERROR"
2250            ) {
2251                let mut cursor = current.walk();
2252                stack.extend(current.named_children(&mut cursor));
2253            }
2254        }
2255    }
2256
2257    fn visit_node<'tree>(
2258        &mut self,
2259        node: Node<'tree>,
2260        scope: &ScopeInfo,
2261        stack: &mut Vec<CppWork<'tree>>,
2262    ) {
2263        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2264            self.visit_node(node, &recovered_scope, stack);
2265            return;
2266        }
2267        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2268        {
2269            let displaced_namespace_items =
2270                displaced_fragment_namespace_geometry(node, self.source)
2271                    .map(|boundary| boundary.namespace_items)
2272                    .unwrap_or_default();
2273            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2274            let mut class_stack = Vec::new();
2275            // When the full body cannot be safely reparsed, the original class
2276            // node still proves ownership for its parser-visible prefix.
2277            let parser_visible_body =
2278                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2279                    .then(|| cpp_body_node(class_node))
2280                    .flatten();
2281            let class_unit = self.visit_named_class_like_shape(
2282                class_node,
2283                name,
2284                parser_visible_body,
2285                true,
2286                Some(fragmented.class_range),
2287                Some(extract_cpp_supertypes(class_node, self.source)),
2288                scope,
2289                &mut class_stack,
2290            );
2291            let member_scope = ScopeInfo {
2292                package_name: class_unit.package_name().to_string(),
2293                module: scope.module.clone(),
2294                class_unit: Some(class_unit.clone()),
2295                template_signature: scope.template_signature.clone(),
2296                template_metadata: None,
2297                declarations_are_fields: true,
2298                recovered_specialization_member_scope: false,
2299                visible_using_namespaces: scope.visible_using_namespaces.clone(),
2300            };
2301            let complete = outcome.is_some_and(|outcome| {
2302                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2303            });
2304            if complete {
2305                self.consumed_fragment_regions
2306                    .push((node.start_byte(), fragmented.class_range.end_byte));
2307            } else {
2308                // A macro-constrained member can make the full body reparse
2309                // unsafe while tree-sitter still exposes later class members
2310                // as bounded siblings up to the displaced `}`/`;`. Keep the
2311                // structurally proven class/base declaration and re-own those
2312                // sibling nodes under it. They retain their original parser
2313                // nodes and exact ranges; the close boundary comes solely from
2314                // `fragmented_plain_class_body`.
2315                // Template wrappers put the escaped members beside the
2316                // template rather than beside its malformed declaration.
2317                for candidate in cpp_following_named_siblings(node, self.source) {
2318                    if candidate.start_byte() >= fragmented.reparse_end {
2319                        break;
2320                    }
2321                    if cpp_fragment_sibling_is_class_member(
2322                        candidate,
2323                        fragmented.reparse_end,
2324                        self.source,
2325                    ) {
2326                        self.recovered_class_sibling_scopes
2327                            .insert(candidate.id(), member_scope.clone());
2328                    }
2329                }
2330            }
2331            for item in displaced_namespace_items {
2332                self.recovered_class_sibling_scopes
2333                    .insert(item.id(), scope.clone());
2334            }
2335            stack.extend(class_stack);
2336            return;
2337        }
2338        match node.kind() {
2339            "template_declaration" => {
2340                if let Some(recovered) = recover_fragmented_preprocessor_class(node, self.source) {
2341                    let mut template_scope = scope.clone();
2342                    template_scope.template_signature =
2343                        cpp_template_signature(node, recovered.declaration_node, self.source);
2344                    template_scope.template_metadata =
2345                        cpp_template_metadata(node, recovered.class_node, self.source);
2346                    let raw_supertypes =
2347                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
2348                    let mut class_stack = Vec::new();
2349                    let class_unit = self.visit_named_class_like_shape(
2350                        recovered.class_node,
2351                        recovered.name,
2352                        Some(recovered.body),
2353                        true,
2354                        Some(recovered.range),
2355                        raw_supertypes,
2356                        &template_scope,
2357                        &mut class_stack,
2358                    );
2359                    self.parsed.record_materialization(
2360                        MaterializationRecord::RecoveredDeclaration {
2361                            recovery: recovered.range,
2362                            unit: class_unit.clone(),
2363                        },
2364                    );
2365                    let member_scope = ScopeInfo {
2366                        package_name: template_scope.package_name.clone(),
2367                        module: template_scope.module.clone(),
2368                        class_unit: Some(class_unit.clone()),
2369                        template_signature: template_scope.template_signature.clone(),
2370                        template_metadata: None,
2371                        declarations_are_fields: true,
2372                        recovered_specialization_member_scope: recovered
2373                            .class_node
2374                            .child_by_field_name("name")
2375                            .is_some_and(|name| name.kind() == "template_type"),
2376                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2377                    };
2378                    for tail_member in recovered.tail_members.into_iter().rev() {
2379                        stack.push(CppWork::Node(CppNodeWork {
2380                            node: tail_member,
2381                            scope: member_scope.clone(),
2382                        }));
2383                    }
2384                    stack.extend(class_stack);
2385                    for sibling in recovered.member_siblings {
2386                        self.recovered_class_sibling_scopes
2387                            .insert(sibling.id(), member_scope.clone());
2388                    }
2389                    return;
2390                }
2391                for index in (0..node.named_child_count()).rev() {
2392                    let Some(child) = node.named_child(index) else {
2393                        continue;
2394                    };
2395                    if matches!(
2396                        child.kind(),
2397                        "class_specifier"
2398                            | "struct_specifier"
2399                            | "union_specifier"
2400                            | "enum_specifier"
2401                            | "function_definition"
2402                            | "declaration"
2403                            | "field_declaration"
2404                            | "alias_declaration"
2405                            | "namespace_definition"
2406                    ) {
2407                        let mut template_scope = scope.clone();
2408                        template_scope.template_signature =
2409                            cpp_template_signature(node, child, self.source);
2410                        template_scope.template_metadata =
2411                            cpp_template_metadata(node, child, self.source);
2412                        if let Some(recovered) =
2413                            recover_fragmented_partial_specialization(node, child, self.source)
2414                        {
2415                            let code_unit = self.visit_named_class_like_shape(
2416                                recovered.declaration_node,
2417                                recovered.name,
2418                                None,
2419                                true,
2420                                Some(recovered.range),
2421                                None,
2422                                &template_scope,
2423                                stack,
2424                            );
2425                            self.parsed.record_materialization(
2426                                MaterializationRecord::RecoveredDeclaration {
2427                                    recovery: recovered.range,
2428                                    unit: code_unit.clone(),
2429                                },
2430                            );
2431                            let mut member_scope = template_scope.clone();
2432                            member_scope.class_unit = Some(code_unit);
2433                            member_scope.declarations_are_fields = true;
2434                            member_scope.recovered_specialization_member_scope = true;
2435                            for prefix_member in recovered.prefix_members.into_iter().rev() {
2436                                stack.push(CppWork::Node(CppNodeWork {
2437                                    node: prefix_member,
2438                                    scope: member_scope.clone(),
2439                                }));
2440                            }
2441                            for sibling in recovered.member_siblings {
2442                                self.recovered_class_sibling_scopes
2443                                    .insert(sibling.id(), member_scope.clone());
2444                            }
2445                            for following in recovered.following_declarations.into_iter().rev() {
2446                                stack.push(CppWork::Node(CppNodeWork {
2447                                    node: following,
2448                                    scope: scope.clone(),
2449                                }));
2450                            }
2451                            return;
2452                        }
2453                        stack.push(CppWork::Node(CppNodeWork {
2454                            node: child,
2455                            scope: template_scope,
2456                        }));
2457                    }
2458                }
2459            }
2460            "namespace_definition" => self.visit_namespace(node, scope, stack),
2461            "linkage_specification" => {
2462                if let Some(body) = cpp_body_node(node) {
2463                    stack.push(CppWork::Container(CppContainer {
2464                        node: body,
2465                        scope: scope.clone(),
2466                    }));
2467                } else {
2468                    stack.push(CppWork::Container(CppContainer {
2469                        node,
2470                        scope: scope.clone(),
2471                    }));
2472                }
2473            }
2474            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2475                self.visit_class_like(node, scope, stack)
2476            }
2477            "function_definition" => self.visit_function_definition(node, scope, stack),
2478            // A bare namespace-begin sentinel can make tree-sitter promote the
2479            // wrapped declaration to an ERROR node instead of the usual bogus
2480            // function_definition envelope. Keep the recovery entry point on
2481            // the same structured path for both shapes; ordinary ERROR nodes
2482            // retain their declaration-preserving wrapper traversal when the
2483            // sentinel predicate does not match.
2484            "ERROR" => {
2485                if !self.visit_sentinel_macro_region(node, scope, stack) {
2486                    self.visit_macro_swallowed_function_declarations(node, scope);
2487                    stack.push(CppWork::Container(CppContainer {
2488                        node,
2489                        scope: scope.clone(),
2490                    }));
2491                }
2492            }
2493            "declaration" => {
2494                if scope.class_unit.is_some()
2495                    && scope.declarations_are_fields
2496                    && scope.recovered_specialization_member_scope
2497                    && let Some(alias_name) =
2498                        recovered_using_declaration_alias_name(node, self.source)
2499                {
2500                    self.add_type_aliases(node, scope, vec![alias_name]);
2501                } else {
2502                    self.visit_declaration(node, scope, scope.declarations_are_fields, stack)
2503                }
2504            }
2505            "field_declaration" => self.visit_declaration(node, scope, true, stack),
2506            "type_definition" | "alias_declaration" => {
2507                self.visit_type_declaration(node, scope, stack)
2508            }
2509            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2510            "preproc_include" => self.visit_include(node),
2511            kind if preserves_declaration_scope_through_wrapper(
2512                kind,
2513                scope.class_unit.is_some(),
2514            ) =>
2515            {
2516                // A preprocessor conditional gates every declaration inside it
2517                // on a configuration this analyzer never evaluates; record the
2518                // interval so declaration state can say so (issue #1476). The
2519                // else/elif branches are children of the `preproc_if` node, so
2520                // recording the openers covers every branch.
2521                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2522                    let mut range = cpp_declaration_range(node);
2523                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2524                        range.end_byte = boundary.end_byte;
2525                        range.end_line = boundary.end_line;
2526                    }
2527                    self.parsed.record_materialization(
2528                        MaterializationRecord::ConfigurationConditional { range },
2529                    );
2530                }
2531                stack.push(CppWork::Container(CppContainer {
2532                    node,
2533                    scope: scope.clone(),
2534                }))
2535            }
2536            _ => {}
2537        }
2538    }
2539
2540    fn visit_macro_swallowed_function_declarations(
2541        &mut self,
2542        envelope: Node<'_>,
2543        scope: &ScopeInfo,
2544    ) {
2545        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2546            || envelope.kind() == "ERROR"
2547                && envelope
2548                    .parent()
2549                    .is_some_and(|parent| parent.kind() == "ERROR")
2550        {
2551            return;
2552        }
2553        let mut stack = (0..envelope.named_child_count())
2554            .filter_map(|index| envelope.named_child(index))
2555            .collect::<Vec<_>>();
2556        while let Some(node) = stack.pop() {
2557            if node.kind() == "function_declarator" {
2558                self.visit_error_swallowed_function_declaration(node, scope);
2559            }
2560            for index in 0..node.named_child_count() {
2561                if let Some(child) = node.named_child(index) {
2562                    stack.push(child);
2563                }
2564            }
2565        }
2566    }
2567
2568    fn visit_error_swallowed_function_declaration(
2569        &mut self,
2570        node: Node<'_>,
2571        scope: &ScopeInfo,
2572    ) -> bool {
2573        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2574            return false;
2575        };
2576        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2577            return false;
2578        };
2579        let root = tree.root_node();
2580        let mut cursor = root.walk();
2581        let declarations = root
2582            .named_children(&mut cursor)
2583            .filter(|child| child.kind() != "comment")
2584            .collect::<Vec<_>>();
2585        let [declaration] = declarations.as_slice() else {
2586            return false;
2587        };
2588        if declaration.kind() != "declaration"
2589            || declaration.has_error()
2590            || declaration.start_byte() != start
2591            || declaration.end_byte() != end
2592        {
2593            return false;
2594        }
2595        let recovery = cpp_recovery_window(self.source, start, end);
2596        self.record_recovered_declarations(recovery, |visitor| {
2597            visitor.run_container_work(root, scope.clone());
2598        });
2599        true
2600    }
2601
2602    fn visit_namespace<'tree>(
2603        &mut self,
2604        node: Node<'tree>,
2605        scope: &ScopeInfo,
2606        stack: &mut Vec<CppWork<'tree>>,
2607    ) {
2608        let name_node = node.child_by_field_name("name");
2609        let Some(name_node) = name_node else {
2610            if let Some(body) = cpp_body_node(node) {
2611                stack.push(CppWork::Container(CppContainer {
2612                    node: body,
2613                    scope: scope.clone(),
2614                }));
2615            }
2616            return;
2617        };
2618        // Diagnostic corpora contain deliberately ill-formed global namespace
2619        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2620        // the leading global `::` as the first anonymous child. Honor that AST
2621        // boundary instead of appending the name to the lexical namespace;
2622        // appending produced legacy names such as `outer::::outer::inner`, which
2623        // could not round-trip through the structured FqName boundary.
2624        let explicitly_global = name_node
2625            .child(0)
2626            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2627        let components = cpp_namespace_name_components(name_node, self.source);
2628        if components.is_empty() {
2629            return;
2630        }
2631        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2632        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2633        // shorthand must declare `a` as well as `a::b` -- extracting only the
2634        // innermost level left the enclosing namespace undeclared and made the
2635        // two spellings of one construct disagree (issue #1878).
2636        let mut package_name = if explicitly_global {
2637            String::new()
2638        } else {
2639            scope.package_name.clone()
2640        };
2641        let mut module = None;
2642        for component in components {
2643            let full_name = if package_name.is_empty() {
2644                component
2645            } else {
2646                format!("{package_name}::{component}")
2647            };
2648            let level = CodeUnit::new_fq(
2649                self.file.clone(),
2650                CodeUnitType::Module,
2651                "",
2652                full_name.clone(),
2653                cpp_namespace_fq(&full_name),
2654            );
2655            if !self.parsed.contains_declaration(&level) {
2656                self.parsed
2657                    .add_code_unit(level.clone(), node, self.source, None, None);
2658            }
2659            package_name = full_name;
2660            module = Some(level);
2661        }
2662
2663        let namespace_scope = ScopeInfo {
2664            package_name,
2665            module,
2666            // C++ never nests a namespace inside a class, so a surviving
2667            // class_unit here is always recovery bleed: a malformed-region
2668            // boundary upstream mis-scoped this namespace block. Keeping the
2669            // owner would mint the namespace's declarations as class members
2670            // under a re-appended package, desyncing the fq boundary assert
2671            // (#2306). Dropping it is identity-neutral for valid code, where
2672            // class_unit is always empty at a namespace definition.
2673            class_unit: None,
2674            template_signature: scope.template_signature.clone(),
2675            template_metadata: scope.template_metadata.clone(),
2676            declarations_are_fields: false,
2677            recovered_specialization_member_scope: false,
2678            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2679        };
2680        let container = cpp_body_node(node).unwrap_or(node);
2681        stack.push(CppWork::Container(CppContainer {
2682            node: container,
2683            scope: namespace_scope,
2684        }));
2685    }
2686
2687    fn visit_class_like<'tree>(
2688        &mut self,
2689        node: Node<'tree>,
2690        scope: &ScopeInfo,
2691        stack: &mut Vec<CppWork<'tree>>,
2692    ) {
2693        let Some(name) = class_like_name(node, self.source) else {
2694            return;
2695        };
2696        let name = qualified_class_name_chain(node, self.source, scope)
2697            .map(|chain| chain.join("$"))
2698            .unwrap_or(name);
2699        self.visit_named_class_like(node, name, scope, stack);
2700    }
2701
2702    fn visit_named_class_like<'tree>(
2703        &mut self,
2704        node: Node<'tree>,
2705        name: String,
2706        scope: &ScopeInfo,
2707        stack: &mut Vec<CppWork<'tree>>,
2708    ) {
2709        let body = cpp_body_node(node);
2710        let definition_body_present = body.is_some();
2711        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2712            .then(|| extract_cpp_supertypes(node, self.source));
2713        self.visit_named_class_like_shape(
2714            node,
2715            name,
2716            body,
2717            definition_body_present,
2718            None,
2719            raw_supertypes,
2720            scope,
2721            stack,
2722        );
2723    }
2724
2725    /// Whether this class-like declaration is a C tag that belongs to the
2726    /// enclosing non-aggregate scope rather than to the aggregate it is
2727    /// lexically written inside.
2728    ///
2729    /// `class_specifier` is deliberately excluded: `class` is not C, so text
2730    /// that spells one in a `.c` file is not C code and keeps the C++ reading
2731    /// rather than getting a half-C identity.
2732    fn mints_tag_at_enclosing_c_scope(
2733        &self,
2734        declaration_node: Node<'_>,
2735        scope: &ScopeInfo,
2736    ) -> bool {
2737        self.c_tag_semantics
2738            && scope.class_unit.is_some()
2739            && matches!(
2740                declaration_node.kind(),
2741                "struct_specifier" | "union_specifier" | "enum_specifier"
2742            )
2743    }
2744
2745    #[allow(clippy::too_many_arguments)]
2746    fn visit_named_class_like_shape<'tree>(
2747        &mut self,
2748        declaration_node: Node<'tree>,
2749        name: String,
2750        body: Option<Node<'tree>>,
2751        definition_body_present: bool,
2752        explicit_range: Option<Range>,
2753        raw_supertypes: Option<Vec<String>>,
2754        scope: &ScopeInfo,
2755        stack: &mut Vec<CppWork<'tree>>,
2756    ) -> CodeUnit {
2757        let displaced_macro_tail = if explicit_range.is_none() {
2758            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2759        } else {
2760            None
2761        };
2762        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2763        let recovered_scope = self.scope_for_recovered_exported_class(
2764            declaration_node,
2765            &name,
2766            definition_body_present,
2767            scope,
2768        );
2769        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
2770        // aggregate's member list is declared at the enclosing non-aggregate
2771        // scope, not nested inside the aggregate. `scope.class_unit` is the
2772        // only aggregate carrier in this walk, so dropping it puts the tag at
2773        // the nearest enclosing non-aggregate scope -- the module at file or
2774        // namespace scope, and the same block-scope representation a
2775        // function-local aggregate already gets. The tag's own body scope
2776        // below still owns its members, so fields and enumerators are
2777        // unaffected.
2778        let c_tag_scope;
2779        let scope = if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope) {
2780            c_tag_scope = ScopeInfo {
2781                class_unit: None,
2782                ..recovered_scope.clone()
2783            };
2784            &c_tag_scope
2785        } else {
2786            &recovered_scope
2787        };
2788        let short_name = if let Some(parent) = &scope.class_unit {
2789            cpp_join_nested_short(parent.short_name(), &name)
2790        } else {
2791            name.clone()
2792        };
2793        // A top-level out-of-line qualified class definition (`struct
2794        // Outer::Inner { ... }` inside its namespace, #2246) carries its
2795        // nesting chain as the `$`-joined display name; push one Type/Nested
2796        // segment per class so segment-pop owner navigation keeps working.
2797        // Every other leaf name stays opaque so a literal `$` in a source
2798        // identifier never crosses the split/join boundary (#2140).
2799        let qualified_chain = if scope.class_unit.is_none() {
2800            qualified_class_name_chain(declaration_node, self.source, scope)
2801                .filter(|chain| chain.join("$") == name)
2802        } else {
2803            None
2804        };
2805        let fq = if let Some(chain) = qualified_chain {
2806            let mut fq = FqName::new();
2807            cpp_push_package(&mut fq, &scope.package_name);
2808            let mut first = true;
2809            for component in chain {
2810                let kind = if first {
2811                    SegmentKind::Type
2812                } else {
2813                    SegmentKind::Nested
2814                };
2815                fq.push(cpp_segment(&component, kind));
2816                first = false;
2817            }
2818            fq
2819        } else {
2820            cpp_leaf_fq(
2821                &scope.package_name,
2822                scope.class_unit.as_ref(),
2823                &name,
2824                SegmentKind::Nested,
2825                SegmentKind::Type,
2826            )
2827        };
2828        let code_unit = CodeUnit::with_signature_and_fq(
2829            self.file.clone(),
2830            CodeUnitType::Class,
2831            scope.package_name.clone(),
2832            short_name,
2833            scope.template_signature.clone(),
2834            false,
2835            fq,
2836        );
2837        let has_body = definition_body_present;
2838        if !has_body && self.parsed.contains_declaration(&code_unit) {
2839            self.parsed.record_navigation_range(
2840                code_unit.clone(),
2841                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2842            );
2843            return code_unit;
2844        }
2845        if has_body {
2846            if let Some(range) = explicit_range {
2847                self.parsed
2848                    .replace_code_unit_with_range(code_unit.clone(), range, None, None);
2849            } else {
2850                self.parsed.replace_code_unit(
2851                    code_unit.clone(),
2852                    declaration_node,
2853                    self.source,
2854                    None,
2855                    None,
2856                );
2857            }
2858        } else {
2859            self.parsed
2860                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2861        }
2862        if let Some(raw_supertypes) = raw_supertypes {
2863            self.parsed
2864                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2865        }
2866        self.parsed.add_signature(
2867            code_unit.clone(),
2868            render_cpp_type_signature(
2869                declaration_node,
2870                self.source,
2871                scope.template_signature.as_deref(),
2872            ),
2873        );
2874        if let Some(metadata) = &scope.template_metadata {
2875            let primary_short_name = if let Some(parent) = &scope.class_unit {
2876                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2877            } else {
2878                metadata.primary_name.clone()
2879            };
2880            let primary_fq_name = CodeUnit::new(
2881                self.file.clone(),
2882                CodeUnitType::Class,
2883                scope.package_name.clone(),
2884                primary_short_name,
2885            )
2886            .fq_name();
2887            let mut metadata = metadata.clone();
2888            metadata.primary_fq_name = primary_fq_name;
2889            self.parsed
2890                .set_cpp_template_metadata(code_unit.clone(), metadata);
2891        }
2892        if let Some(parent) = &scope.class_unit {
2893            self.parsed.add_child(parent.clone(), code_unit.clone());
2894        } else if let Some(module) = &scope.module {
2895            self.parsed.add_child(module.clone(), code_unit.clone());
2896        }
2897
2898        if let Some(body) = body {
2899            let mut nested_scope = scope.clone();
2900            nested_scope.class_unit = Some(code_unit.clone());
2901            nested_scope.template_signature = scope.template_signature.clone();
2902            // Template metadata describes the class just created. It must not
2903            // leak into ordinary nested declarations in that class's body.
2904            // Recovered export-macro specializations carry a separate scope bit
2905            // for their declaration-shaped body members.
2906            nested_scope.template_metadata = None;
2907            // Export-macro class bodies recovered from a function_definition use
2908            // compound_statement children, whose direct fields are declarations.
2909            nested_scope.recovered_specialization_member_scope =
2910                scope.template_metadata.as_ref().is_some_and(|metadata| {
2911                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
2912                });
2913            nested_scope.declarations_are_fields =
2914                is_recovered_exported_class_container(declaration_node, self.source)
2915                    || nested_scope.recovered_specialization_member_scope;
2916            if let Some(displaced) = displaced_macro_tail {
2917                // A macro-shaped field without a source semicolon can make
2918                // tree-sitter consume the real class terminator as an ERROR
2919                // inside that field, then retain following namespace items as
2920                // later field-list children. Drain the proven class prefix
2921                // first and re-own only the structured tail with the outer
2922                // scope. The tail is pushed first because the work stack is
2923                // LIFO.
2924                push_cpp_sibling_range(
2925                    body,
2926                    displaced.split_index,
2927                    usize::MAX,
2928                    scope.clone(),
2929                    stack,
2930                );
2931                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
2932            } else {
2933                stack.push(CppWork::Container(CppContainer {
2934                    node: body,
2935                    scope: nested_scope,
2936                }));
2937            }
2938        }
2939        if declaration_node.kind() == "enum_specifier" {
2940            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
2941            if !self.has_enum_enumerator_units(&code_unit) {
2942                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
2943            }
2944        }
2945        code_unit
2946    }
2947
2948    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
2949        let prefix = format!("{}.", parent.short_name());
2950        let parent_short = parent.short_name();
2951        self.parsed.declarations().iter().any(|unit| {
2952            unit.kind() == CodeUnitType::Field
2953                && unit.source() == parent.source()
2954                && unit.package_name() == parent.package_name()
2955                && if parent_short.is_empty() {
2956                    // Anonymous enum/union parent: its enumerators carry bare
2957                    // short names (#2140), so presence means any ownerless
2958                    // field in this file.
2959                    !unit.short_name().contains(['.', '$'])
2960                } else {
2961                    unit.short_name().starts_with(&prefix)
2962                }
2963        })
2964    }
2965
2966    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
2967        walk_named_tree_preorder(node, false, |child| {
2968            if child.kind() != "enumerator" {
2969                return WalkControl::Continue;
2970            }
2971            let Some(name_node) = child.child_by_field_name("name") else {
2972                return WalkControl::Continue;
2973            };
2974            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
2975            if name.is_empty() {
2976                return WalkControl::Continue;
2977            }
2978            let code_unit = CodeUnit::new_fq(
2979                self.file.clone(),
2980                CodeUnitType::Field,
2981                scope.package_name.clone(),
2982                cpp_join_member_short(parent.short_name(), &name),
2983                parent
2984                    .fq()
2985                    .clone()
2986                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
2987            );
2988            if self.parsed.contains_declaration(&code_unit) {
2989                return WalkControl::Continue;
2990            }
2991            self.parsed.add_code_unit(
2992                code_unit.clone(),
2993                child,
2994                self.source,
2995                Some(parent.clone()),
2996                None,
2997            );
2998            self.parsed.add_signature(
2999                code_unit,
3000                normalize_cpp_whitespace(node_text(child, self.source)),
3001            );
3002            WalkControl::Continue
3003        });
3004    }
3005
3006    fn visit_enum_enumerators_from_text(
3007        &mut self,
3008        node: Node<'_>,
3009        scope: &ScopeInfo,
3010        parent: &CodeUnit,
3011    ) {
3012        let text = node_text(node, self.source);
3013        let Some((_, body)) = text.split_once('{') else {
3014            return;
3015        };
3016        let Some((body, _)) = body.rsplit_once('}') else {
3017            return;
3018        };
3019        for entry in body.split(',') {
3020            let trimmed = entry.trim();
3021            let name = trimmed
3022                .split('=')
3023                .next()
3024                .unwrap_or("")
3025                .split_whitespace()
3026                .next()
3027                .unwrap_or("");
3028            if name.is_empty() {
3029                continue;
3030            }
3031            let code_unit = CodeUnit::new_fq(
3032                self.file.clone(),
3033                CodeUnitType::Field,
3034                scope.package_name.clone(),
3035                cpp_join_member_short(parent.short_name(), name),
3036                parent
3037                    .fq()
3038                    .clone()
3039                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
3040            );
3041            if self.parsed.contains_declaration(&code_unit) {
3042                continue;
3043            }
3044            self.parsed.add_code_unit(
3045                code_unit.clone(),
3046                node,
3047                self.source,
3048                Some(parent.clone()),
3049                None,
3050            );
3051            self.parsed.add_signature(code_unit, trimmed.to_string());
3052        }
3053    }
3054
3055    fn visit_function_definition<'tree>(
3056        &mut self,
3057        node: Node<'tree>,
3058        scope: &ScopeInfo,
3059        stack: &mut Vec<CppWork<'tree>>,
3060    ) {
3061        // A file-scope object-like macro sentinel the parser cannot see (issue
3062        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
3063        // prefixes as a bogus `function_definition` that swallows real namespaces,
3064        // classes, and members. Reparse the swallowed interior as C++ items so the
3065        // ordinary declaration visitors index it with byte/line-exact ownership.
3066        if self.visit_sentinel_macro_region(node, scope, stack) {
3067            return;
3068        }
3069        if node.has_error() {
3070            self.visit_macro_swallowed_function_declarations(node, scope);
3071        }
3072        if let Some((class_node, name, raw_supertypes)) =
3073            recover_exported_class_function_definition(node, self.source)
3074        {
3075            let body = cpp_body_node(class_node);
3076            let displaced_namespace = cpp_body_node(node)
3077                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3078            let fragmented = cpp_body_node(node).and_then(|body| {
3079                fragmented_export_function_body_region(
3080                    node,
3081                    body,
3082                    self.source,
3083                    displaced_namespace.as_ref(),
3084                )
3085            });
3086            // The recovery tuple's first node is the class-like type when the
3087            // parser exposes one, but the synthetic wrapper owns the compound
3088            // statement that contains the truncated class body. Use the
3089            // wrapper body for fragmented-member detection; retain the
3090            // class-node body for the ordinary (non-fragmented) path below.
3091            if let Some(fragmented) = fragmented {
3092                // The lifted sibling no longer sits below the parser-visible
3093                // namespace node. Restore the current parent scope when the
3094                // ordinary work walk reaches that class.
3095                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3096                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3097                {
3098                    let mut boundary_scope = scope.clone();
3099                    for sibling in cpp_following_named_siblings(node, self.source) {
3100                        if sibling.start_byte() >= boundary.start_byte() {
3101                            break;
3102                        }
3103                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3104                            boundary_scope.visible_using_namespaces.push(namespace);
3105                        }
3106                    }
3107                    self.recovered_class_sibling_scopes
3108                        .insert(boundary.id(), boundary_scope);
3109                }
3110                let mut recovered_constructor = None;
3111                let mut recovered_prefix_tree = None;
3112                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3113                {
3114                    Some(FragmentedExportMembers::Complete(tree)) => {
3115                        if let Some(body) = body
3116                            && let Some(range) =
3117                                cpp_reparsed_synthetic_initializer_constructor_range(
3118                                    tree.root_node(),
3119                                    &name,
3120                                    self.source,
3121                                    body.end_byte(),
3122                                )
3123                        {
3124                            recovered_constructor = Some(range);
3125                            recovered_prefix_tree = Some(tree);
3126                            None
3127                        } else {
3128                            Some(FragmentedExportMembers::Complete(tree))
3129                        }
3130                    }
3131                    outcome => outcome,
3132                };
3133                let mut class_stack = Vec::new();
3134                let class_unit = self.visit_named_class_like_shape(
3135                    class_node,
3136                    name,
3137                    None,
3138                    true,
3139                    Some(fragmented.class_range),
3140                    raw_supertypes,
3141                    scope,
3142                    &mut class_stack,
3143                );
3144                self.parsed
3145                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3146                        recovery: fragmented.class_range,
3147                        unit: class_unit.clone(),
3148                    });
3149                let complete = outcome.is_some_and(|outcome| {
3150                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3151                });
3152                if complete {
3153                    self.consumed_fragment_regions
3154                        .push((node.start_byte(), fragmented.class_range.end_byte));
3155                } else {
3156                    // The reparse can fail when the first constructor or a
3157                    // method body is split into statement-shaped siblings.
3158                    // Keep the recovered class envelope, but do not visit the
3159                    // synthetic wrapper body: its initializer expressions can
3160                    // look like same-named member functions (for example
3161                    // `Token.location(loc)`). Re-own only the original sibling
3162                    // nodes that fall inside the proven class range. Their CST
3163                    // shapes retain the real field/function kinds and ranges.
3164                    let member_scope = ScopeInfo {
3165                        package_name: class_unit.package_name().to_string(),
3166                        module: scope.module.clone(),
3167                        class_unit: Some(class_unit.clone()),
3168                        template_signature: scope.template_signature.clone(),
3169                        template_metadata: None,
3170                        declarations_are_fields: true,
3171                        recovered_specialization_member_scope: false,
3172                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
3173                    };
3174                    for candidate in cpp_following_named_siblings(node, self.source) {
3175                        if candidate.start_byte() >= fragmented.reparse_end {
3176                            break;
3177                        }
3178                        if cpp_fragment_sibling_is_class_member(
3179                            candidate,
3180                            fragmented.reparse_end,
3181                            self.source,
3182                        ) {
3183                            self.recovered_class_sibling_scopes
3184                                .insert(candidate.id(), member_scope.clone());
3185                        }
3186                    }
3187                    if let Some(range) = recovered_constructor
3188                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3189                    {
3190                        self.visit_recovered_fragment_prefix_members(
3191                            prefix_tree.root_node(),
3192                            range.start,
3193                            &class_unit,
3194                            scope,
3195                        );
3196                        self.visit_recovered_fragment_constructor(
3197                            range,
3198                            body,
3199                            class_node,
3200                            &class_unit,
3201                            scope,
3202                        );
3203                    }
3204                }
3205                if let Some(boundary) = displaced_namespace {
3206                    for item in boundary.namespace_items {
3207                        self.recovered_class_sibling_scopes
3208                            .insert(item.id(), scope.clone());
3209                    }
3210                }
3211                stack.extend(class_stack);
3212                return;
3213            }
3214            let mut stack = Vec::new();
3215            let class_unit = self.visit_named_class_like_shape(
3216                class_node,
3217                name,
3218                body,
3219                body.is_some(),
3220                None,
3221                raw_supertypes,
3222                scope,
3223                &mut stack,
3224            );
3225            self.parsed
3226                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3227                    recovery: cpp_declaration_range(node),
3228                    unit: class_unit,
3229                });
3230            // Issue #1524: the bogus `function_definition` body can run past
3231            // the class's true closing brace (the parse ends it with a
3232            // zero-width `MISSING "}"`), swallowing following namespace-scope
3233            // siblings -- they would index as members of the recovered class.
3234            // When the body's text-balanced close lands before the body's own
3235            // end, re-own the swallowed tail with the outer scope instead.
3236            if let Some(body) = body
3237                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3238                && class_close < body.end_byte()
3239            {
3240                let split = {
3241                    let mut cursor = body.walk();
3242                    body.named_children(&mut cursor)
3243                        .position(|child| child.start_byte() > class_close)
3244                };
3245                if let Some(split) = split {
3246                    // The seeded work is a single Container over the whole
3247                    // body with the class scope; replace it with the bounded
3248                    // head (class scope) plus the swallowed tail (outer
3249                    // scope). Push tail first so the head drains first.
3250                    let seeded = stack.pop();
3251                    match seeded {
3252                        Some(CppWork::Container(container)) => {
3253                            push_cpp_sibling_range(
3254                                body,
3255                                split,
3256                                usize::MAX,
3257                                scope.clone(),
3258                                &mut stack,
3259                            );
3260                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3261                        }
3262                        // visit_named_class_like_shape always seeds exactly
3263                        // one Container when a body is present.
3264                        _ => unreachable!("exported-class seed is always one Container"),
3265                    }
3266                }
3267            }
3268            while let Some(work) = stack.pop() {
3269                match work {
3270                    CppWork::Container(container) => {
3271                        push_cpp_container_work(container.node, container.scope, &mut stack);
3272                    }
3273                    CppWork::Siblings(siblings) => {
3274                        advance_cpp_siblings(siblings, self.source, &mut stack);
3275                    }
3276                    CppWork::Node(work) => self.visit_node(work.node, &work.scope, &mut stack),
3277                }
3278            }
3279            return;
3280        }
3281        let recovered_constraint_constructor =
3282            cpp_recovered_template_macro_constructor(node, self.source);
3283        let declarator = recovered_constraint_constructor
3284            .map(|(declarator, _)| declarator)
3285            .or_else(|| node.child_by_field_name("declarator"));
3286        let Some(declarator) = declarator else {
3287            self.visit_malformed_function_definition_container(node, scope, stack);
3288            return;
3289        };
3290        let Some(function_declarator) = extract_function_declarator(declarator) else {
3291            self.visit_malformed_function_definition_container(node, scope, stack);
3292            return;
3293        };
3294        let function = if let Some((_, callable_name)) =
3295            cpp_macro_displaced_callable_parts(function_declarator, self.source)
3296        {
3297            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3298        } else {
3299            extract_function_info(function_declarator, self.source, scope)
3300        };
3301        let Some(mut function) = function else {
3302            self.visit_malformed_function_definition_container(node, scope, stack);
3303            return;
3304        };
3305        if let Some((_, template_parameter)) = recovered_constraint_constructor {
3306            function.signature = format!(
3307                "template <{}>{}",
3308                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3309                function.signature
3310            );
3311        }
3312        let code_unit = function.code_unit(self.file.clone());
3313        // Keep an earlier same-file prototype as another physical occurrence
3314        // of this callable. `CodeUnit` already identifies the role-neutral
3315        // overload, while ranges and signature metadata describe its
3316        // declaration/definition occurrences.
3317        self.parsed
3318            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3319        let signature = if recovered_constraint_constructor.is_some() {
3320            normalize_cpp_whitespace(node_text(function_declarator, self.source))
3321        } else {
3322            render_cpp_function_display_signature_from_node(
3323                node,
3324                self.source,
3325                scope.template_signature.as_deref(),
3326                true,
3327            )
3328        };
3329        self.parsed.add_signature_with_metadata(
3330            code_unit.clone(),
3331            cpp_signature_metadata(signature, function_declarator, self.source)
3332                .with_declaration_only(false)
3333                .with_callable_linkage(cpp_callable_linkage(node, self.source)),
3334        );
3335        if let Some(parent) = &scope.class_unit {
3336            self.parsed.add_child(parent.clone(), code_unit);
3337        } else if let Some(module) = &scope.module {
3338            self.parsed.add_child(module.clone(), code_unit);
3339        }
3340    }
3341
3342    /// Recover the namespace lost when tree-sitter promotes an export-macro
3343    /// class definition to a root-level `function_definition`.  Only a
3344    /// body-bearing, top-level recovery may borrow a namespace, and only when
3345    /// one earlier namespace-scope forward declaration proves the identity.
3346    fn scope_for_recovered_exported_class(
3347        &self,
3348        node: Node<'_>,
3349        name: &str,
3350        definition_body_present: bool,
3351        scope: &ScopeInfo,
3352    ) -> ScopeInfo {
3353        if !definition_body_present
3354            || !scope.package_name.is_empty()
3355            || scope.class_unit.is_some()
3356            || !(is_recovered_exported_class_container(node, self.source)
3357                || matches!(node.kind(), "declaration" | "field_declaration")
3358                    && recover_exported_class_declaration(node, self.source).is_some()
3359                || matches!(
3360                    node.kind(),
3361                    "class_specifier" | "struct_specifier" | "union_specifier"
3362                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3363                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3364                        name_node,
3365                        self.source,
3366                    )))
3367                }) || node.parent().is_some_and(|parent| {
3368                    matches!(parent.kind(), "declaration" | "field_declaration")
3369                        && recover_exported_class_declaration(parent, self.source).is_some()
3370                        || is_recovered_exported_class_container(parent, self.source)
3371                })) && class_like_name(node, self.source).as_deref() == Some(name))
3372        {
3373            return scope.clone();
3374        }
3375        let Some(package_name) = unique_earlier_cpp_namespace_forward(node, name, self.source)
3376        else {
3377            return scope.clone();
3378        };
3379
3380        let module = CodeUnit::new_fq(
3381            self.file.clone(),
3382            CodeUnitType::Module,
3383            "",
3384            package_name.clone(),
3385            cpp_namespace_fq(&package_name),
3386        );
3387        let mut recovered = scope.clone();
3388        recovered.package_name = package_name;
3389        recovered.module = Some(module);
3390        recovered
3391    }
3392
3393    fn visit_malformed_function_definition_container<'tree>(
3394        &mut self,
3395        node: Node<'tree>,
3396        scope: &ScopeInfo,
3397        stack: &mut Vec<CppWork<'tree>>,
3398    ) {
3399        let Some(body) = cpp_body_node(node) else {
3400            return;
3401        };
3402        if !cpp_contains_namespace_definition(body) {
3403            return;
3404        }
3405        stack.push(CppWork::Container(CppContainer {
3406            node: body,
3407            scope: scope.clone(),
3408        }));
3409    }
3410
3411    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
3412    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
3413    /// emits for a sentinel-prefixed region, reparse the interior after the
3414    /// sentinel identifier as real C++ items -- confined to the region so
3415    /// every reparsed node keeps its original byte/line position -- and run the
3416    /// ordinary container visitation over the result. Returns `true` when it fired
3417    /// (the caller must then skip normal function processing). Nested sentinel
3418    /// regions recover recursively: the reparsed interior is walked through the
3419    /// same `visit_function_definition` path, so a sentinel inside the region hits
3420    /// this recovery again.
3421    /// Runs `reparse_walk` and records every declaration it mints as a
3422    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
3423    /// `recovery` (issue #1657). A reparsed sentinel region has no single
3424    /// recovered envelope unit: the ordinary visitors mint namespaces,
3425    /// classes, and members directly from the reparsed tree, so the walk's
3426    /// declaration delta is the recovered set. Records are ordered by
3427    /// declaration start byte so the parse product stays deterministic.
3428    fn record_recovered_declarations(
3429        &mut self,
3430        recovery: Range,
3431        reparse_walk: impl FnOnce(&mut Self),
3432    ) {
3433        let before = self.parsed.declarations().clone();
3434        reparse_walk(self);
3435        let mut minted: Vec<CodeUnit> = self
3436            .parsed
3437            .declarations()
3438            .iter()
3439            .filter(|unit| !before.contains(*unit))
3440            .cloned()
3441            .collect();
3442        minted.sort_by_cached_key(|unit| {
3443            let start = self
3444                .parsed
3445                .declaration_ranges(unit)
3446                .first()
3447                .map(|range| range.start_byte)
3448                .unwrap_or(usize::MAX);
3449            (start, unit.fq_name().to_string())
3450        });
3451        for unit in minted {
3452            self.parsed
3453                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3454                    recovery,
3455                    unit,
3456                });
3457        }
3458    }
3459
3460    fn visit_sentinel_macro_region<'tree>(
3461        &mut self,
3462        node: Node<'tree>,
3463        scope: &ScopeInfo,
3464        stack: &mut Vec<CppWork<'tree>>,
3465    ) -> bool {
3466        if self.visit_nested_namespace_sentinel(node, scope) {
3467            return true;
3468        }
3469        if let Some((
3470            reparse_start,
3471            class_start,
3472            body_start,
3473            class_close_start,
3474            class_close_end,
3475            class_close_line,
3476        )) = cpp_sentinel_macro_class_region(node, self.source)
3477        {
3478            let Some(class_tree) =
3479                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3480            else {
3481                return false;
3482            };
3483            let class_root = class_tree.root_node();
3484            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3485            let Some(reparsed_class) =
3486                cpp_sentinel_reparsed_class(class_root, template_node, self.source)
3487            else {
3488                return false;
3489            };
3490            let class_node = reparsed_class.declaration_node;
3491            let name = reparsed_class.name;
3492            let mut class_scope = scope.clone();
3493            if let Some(template_node) = template_node {
3494                class_scope.template_signature =
3495                    cpp_template_signature(template_node, class_node, self.source);
3496                class_scope.template_metadata =
3497                    cpp_template_metadata(template_node, class_node, self.source);
3498            }
3499            let Some(body_tree) =
3500                cpp_reparse_region_items(self.source, body_start, class_close_start)
3501            else {
3502                return false;
3503            };
3504            let raw_supertypes = reparsed_class.raw_supertypes;
3505            let class_range = Range {
3506                start_byte: class_start,
3507                end_byte: class_close_end,
3508                start_line: class_node.start_position().row + 1,
3509                end_line: class_close_line,
3510            };
3511            let class_scope =
3512                self.scope_for_recovered_exported_class(class_node, &name, true, &class_scope);
3513            let mut class_stack = Vec::new();
3514            let class_unit = self.visit_named_class_like_shape(
3515                class_node,
3516                name,
3517                None,
3518                true,
3519                Some(class_range),
3520                raw_supertypes,
3521                &class_scope,
3522                &mut class_stack,
3523            );
3524            self.parsed
3525                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3526                    recovery: class_range,
3527                    unit: class_unit.clone(),
3528                });
3529            let member_scope = ScopeInfo {
3530                package_name: class_scope.package_name.clone(),
3531                module: class_scope.module.clone(),
3532                class_unit: Some(class_unit),
3533                template_signature: class_scope.template_signature.clone(),
3534                template_metadata: None,
3535                declarations_are_fields: true,
3536                recovered_specialization_member_scope: false,
3537                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3538            };
3539            self.run_container_work(body_tree.root_node(), member_scope);
3540            // Register only after the padded body reparse: its nodes deliberately
3541            // retain offsets inside the consumed region and must be visited first.
3542            self.consumed_fragment_regions
3543                .push((node.start_byte(), class_close_end));
3544            // An ERROR envelope can hold real sibling declarations after the
3545            // recovered class's close (the suffix-reparse boundary in
3546            // `cpp_sentinel_macro_class_region` partitions, it does not
3547            // consume). Walk the envelope's remaining children normally; the
3548            // consumed region above keeps the recovered class from being
3549            // indexed twice.
3550            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3551                stack.push(CppWork::Container(CppContainer {
3552                    node,
3553                    scope: scope.clone(),
3554                }));
3555            }
3556            return true;
3557        }
3558        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3559            return false;
3560        };
3561        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3562            return false;
3563        };
3564        let root = tree.root_node();
3565        if !cpp_reparsed_items_are_indexable(root, self.source) {
3566            return false;
3567        }
3568        let recovery = cpp_recovery_window(self.source, start, end);
3569        self.record_recovered_declarations(recovery, |visitor| {
3570            visitor.visit_container(
3571                root,
3572                &scope.package_name,
3573                scope.module.clone(),
3574                scope.class_unit.clone(),
3575                scope.template_signature.clone(),
3576                scope.visible_using_namespaces.clone(),
3577            );
3578        });
3579        if end > node.end_byte() {
3580            self.consumed_fragment_regions
3581                .push((node.start_byte(), end));
3582        } else if node.kind() == "ERROR" && node.end_byte() > end {
3583            // The sentinel region ended at the first recovered class-like item
3584            // but the ERROR envelope keeps real sibling declarations after it
3585            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
3586            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
3587            // envelope's remaining children normally; the consumed region
3588            // keeps the reparsed prefix from being indexed twice.
3589            self.consumed_fragment_regions
3590                .push((node.start_byte(), end));
3591            stack.push(CppWork::Container(CppContainer {
3592                node,
3593                scope: scope.clone(),
3594            }));
3595        }
3596        true
3597    }
3598
3599    /// Re-own complete class declarations from the structured Abseil
3600    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
3601    /// its direct CST children already prove both namespace components and the
3602    /// class bodies, so the ordinary class/member visitor can retain ownership
3603    /// and exact source ranges without admitting unrelated callable bodies.
3604    fn visit_nested_namespace_sentinel(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
3605        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source) else {
3606            return false;
3607        };
3608
3609        let mut package_name = scope.package_name.clone();
3610        let mut module = scope.module.clone();
3611        for component in recovered.namespace_components {
3612            package_name = if package_name.is_empty() {
3613                component
3614            } else {
3615                format!("{package_name}::{component}")
3616            };
3617            let namespace_module = CodeUnit::new_fq(
3618                self.file.clone(),
3619                CodeUnitType::Module,
3620                "",
3621                package_name.clone(),
3622                cpp_namespace_fq(&package_name),
3623            );
3624            if !self.parsed.contains_declaration(&namespace_module) {
3625                self.parsed.add_code_unit(
3626                    namespace_module.clone(),
3627                    recovered.function,
3628                    self.source,
3629                    None,
3630                    None,
3631                );
3632            }
3633            module = Some(namespace_module);
3634        }
3635
3636        let recovered_scope = ScopeInfo {
3637            package_name,
3638            module,
3639            class_unit: scope.class_unit.clone(),
3640            template_signature: scope.template_signature.clone(),
3641            template_metadata: scope.template_metadata.clone(),
3642            declarations_are_fields: false,
3643            recovered_specialization_member_scope: false,
3644            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3645        };
3646        if let Some(fragmented) =
3647            cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, self.source)
3648        {
3649            let mut class_scope = recovered_scope.clone();
3650            if let Some(template_node) = fragmented.template_node {
3651                class_scope.template_signature =
3652                    cpp_template_signature(template_node, fragmented.class_node, self.source);
3653                class_scope.template_metadata =
3654                    cpp_template_metadata(template_node, fragmented.class_node, self.source);
3655            }
3656            if let Some(outcome) = self
3657                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3658            {
3659                let mut class_stack = Vec::new();
3660                let class_unit = self.visit_named_class_like_shape(
3661                    fragmented.class_node,
3662                    fragmented.name.clone(),
3663                    None,
3664                    true,
3665                    Some(fragmented.fragmented.class_range),
3666                    fragmented.raw_supertypes.clone(),
3667                    &class_scope,
3668                    &mut class_stack,
3669                );
3670                self.parsed
3671                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3672                        recovery: fragmented.fragmented.class_range,
3673                        unit: class_unit.clone(),
3674                    });
3675                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3676                    self.consumed_fragment_regions.push((
3677                        fragmented.consumed_start,
3678                        fragmented.fragmented.class_range.end_byte,
3679                    ));
3680                }
3681            }
3682        }
3683        // The class requirement above is the admission gate; once admitted,
3684        // traverse the whole proven inner namespace body so sibling aliases,
3685        // functions, and variables are not silently discarded.
3686        self.run_container_work(recovered.body, recovered_scope);
3687        true
3688    }
3689
3690    fn visit_declaration<'tree>(
3691        &mut self,
3692        node: Node<'tree>,
3693        scope: &ScopeInfo,
3694        in_class_body: bool,
3695        stack: &mut Vec<CppWork<'tree>>,
3696    ) {
3697        if self.visit_sentinel_macro_region(node, scope, stack) {
3698            return;
3699        }
3700        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3701            !cpp_active_template_type_parameter(
3702                node,
3703                node_text(declarator, self.source),
3704                self.source,
3705            )
3706        }) {
3707            return;
3708        }
3709        if in_class_body
3710            && let Some(parent) = scope.class_unit.as_ref()
3711            && let Some(call) =
3712                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3713        {
3714            self.visit_recovered_macro_qualified_constructor_definition(node, call, scope);
3715            return;
3716        }
3717        if in_class_body
3718            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3719        {
3720            self.visit_recovered_macro_qualified_function_declaration(node, call, scope);
3721            return;
3722        }
3723        if in_class_body
3724            && let Some(declarators) =
3725                recovered_macro_qualified_field_declarators(node, self.source)
3726        {
3727            for declarator in declarators {
3728                self.visit_variable_declaration(node, declarator, scope, true);
3729            }
3730            return;
3731        }
3732        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3733        if !recovered_alias_names.is_empty() {
3734            self.add_type_aliases(node, scope, recovered_alias_names);
3735            return;
3736        }
3737
3738        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3739            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3740                // Issue #938: the members tree-sitter scattered out of the fragmented
3741                // multiple-base export node are reparsed from their true body region
3742                // and re-owned as members of the recovered class, with an explicit
3743                // navigation range spanning to the displaced closing brace.
3744                if let Some(outcome) =
3745                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3746                {
3747                    let consumed_region = (
3748                        recovered.declaration_node.end_byte(),
3749                        fragmented.class_range.end_byte,
3750                    );
3751                    let code_unit = self.visit_named_class_like_shape(
3752                        recovered.declaration_node,
3753                        recovered.name,
3754                        None,
3755                        true,
3756                        Some(fragmented.class_range),
3757                        recovered.raw_supertypes,
3758                        scope,
3759                        stack,
3760                    );
3761                    self.parsed.record_materialization(
3762                        MaterializationRecord::RecoveredDeclaration {
3763                            recovery: fragmented.class_range,
3764                            unit: code_unit.clone(),
3765                        },
3766                    );
3767                    let consume_fragment =
3768                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3769                    // Everything between the fragmented declaration and its displaced
3770                    // closing brace now belongs to the recovered class; keep the
3771                    // ordinary walk from re-indexing those scattered siblings at top
3772                    // level. Register the consumed region only after indexing because
3773                    // the reparsed nodes retain byte offsets inside that same region.
3774                    if consume_fragment {
3775                        self.consumed_fragment_regions.push(consumed_region);
3776                    }
3777                    return;
3778                }
3779            }
3780            let uses_initializer_body = recovered.uses_initializer_body;
3781            let definition_body_present = recovered.body.is_some();
3782            let class_unit = self.visit_named_class_like_shape(
3783                recovered.declaration_node,
3784                recovered.name,
3785                recovered.body,
3786                definition_body_present,
3787                None,
3788                recovered.raw_supertypes,
3789                scope,
3790                stack,
3791            );
3792            self.parsed
3793                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3794                    recovery: cpp_declaration_range(node),
3795                    unit: class_unit,
3796                });
3797            if uses_initializer_body {
3798                return;
3799            }
3800        }
3801
3802        let mut handled_function = false;
3803        let mut handled_declarator = false;
3804        let mut cursor = node.walk();
3805        for child in node.named_children(&mut cursor) {
3806            if matches!(
3807                child.kind(),
3808                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3809            ) {
3810                // A named class-like definition remains a declaration even when
3811                // the same statement also declares an object, for example
3812                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3813                // declaration's type and `kind` as its declarator.  Dropping the
3814                // type here loses both its nested owner and every later lexical
3815                // reference to it.  A body is the structured proof that this is
3816                // a definition rather than an elaborated type use such as
3817                // `class Kind value;`.
3818                if cpp_body_node(child).is_some() {
3819                    self.visit_class_like(child, scope, stack);
3820                }
3821                continue;
3822            }
3823        }
3824
3825        let mut cursor = node.walk();
3826        for child in node.children_by_field_name("declarator", &mut cursor) {
3827            if crate::structural::is_recovered_designator_init_declarator(child) {
3828                handled_declarator = true;
3829                continue;
3830            }
3831            if let Some(kind) = classify_declarator(child) {
3832                handled_declarator = true;
3833                match kind {
3834                    DeclaratorKind::Function(function_declarator) => {
3835                        handled_function = true;
3836                        self.visit_function_declaration(node, function_declarator, scope);
3837                    }
3838                    DeclaratorKind::Variable(variable_declarator) => {
3839                        self.visit_variable_declaration(
3840                            node,
3841                            variable_declarator,
3842                            scope,
3843                            in_class_body,
3844                        );
3845                    }
3846                }
3847            }
3848        }
3849
3850        if !handled_declarator {
3851            let mut cursor = node.walk();
3852            for child in node.named_children(&mut cursor) {
3853                if crate::structural::is_recovered_designator_init_declarator(child) {
3854                    handled_declarator = true;
3855                    continue;
3856                }
3857                if !is_unfielded_declarator_candidate(child) {
3858                    continue;
3859                }
3860                let Some(kind) = classify_declarator(child) else {
3861                    continue;
3862                };
3863                handled_declarator = true;
3864                match kind {
3865                    DeclaratorKind::Function(function_declarator) => {
3866                        handled_function = true;
3867                        self.visit_function_declaration(node, function_declarator, scope);
3868                    }
3869                    DeclaratorKind::Variable(variable_declarator) => {
3870                        self.visit_variable_declaration(
3871                            node,
3872                            variable_declarator,
3873                            scope,
3874                            in_class_body,
3875                        );
3876                    }
3877                }
3878            }
3879        }
3880
3881        if handled_function {
3882            return;
3883        }
3884
3885        if !handled_declarator {
3886            if in_class_body {
3887                self.visit_class_members_from_declaration(node, scope);
3888            } else {
3889                self.visit_global_variables_from_declaration(node, scope);
3890            }
3891        }
3892    }
3893
3894    fn visit_function_declaration(
3895        &mut self,
3896        declaration_node: Node<'_>,
3897        declarator: Node<'_>,
3898        scope: &ScopeInfo,
3899    ) {
3900        let Some(function) = extract_function_info(declarator, self.source, scope) else {
3901            return;
3902        };
3903        let code_unit =
3904            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
3905        if self.parsed.contains_declaration(&code_unit) {
3906            self.parsed
3907                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3908            return;
3909        }
3910        self.parsed
3911            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3912        let signature = render_cpp_function_display_signature_from_node(
3913            declaration_node,
3914            self.source,
3915            scope.template_signature.as_deref(),
3916            false,
3917        );
3918        self.parsed.add_signature_with_metadata(
3919            code_unit.clone(),
3920            cpp_signature_metadata(signature, declarator, self.source)
3921                .with_declaration_only(true)
3922                .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source)),
3923        );
3924        if let Some(parent) = &scope.class_unit {
3925            self.parsed.add_child(parent.clone(), code_unit);
3926        } else if let Some(module) = &scope.module {
3927            self.parsed.add_child(module.clone(), code_unit);
3928        }
3929    }
3930
3931    fn visit_recovered_macro_qualified_function_declaration(
3932        &mut self,
3933        declaration_node: Node<'_>,
3934        call: Node<'_>,
3935        scope: &ScopeInfo,
3936    ) {
3937        let Some(parent) = &scope.class_unit else {
3938            return;
3939        };
3940        let Some(name_node) = call.child_by_field_name("function") else {
3941            return;
3942        };
3943        let Some(arguments) = call.child_by_field_name("arguments") else {
3944            return;
3945        };
3946        let Some((signature, parameter_labels)) =
3947            recovered_macro_qualified_function_parameters(arguments, self.source)
3948        else {
3949            return;
3950        };
3951        let arity = parameter_labels.len();
3952        let function = FunctionInfo {
3953            package_name: scope.package_name.clone(),
3954            owner: Some(CppMemberOwner::Unit(parent.clone())),
3955            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
3956            signature,
3957        };
3958        if function.name.is_empty() {
3959            return;
3960        }
3961        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
3962        if self.parsed.contains_declaration(&code_unit) {
3963            self.parsed
3964                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
3965            return;
3966        }
3967        self.parsed
3968            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
3969        let signature_label = render_cpp_function_display_signature_from_node(
3970            declaration_node,
3971            self.source,
3972            scope.template_signature.as_deref(),
3973            false,
3974        );
3975        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
3976            .with_declaration_only(true)
3977            .with_callable_arity(CallableArity::exact(arity))
3978            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
3979        self.parsed
3980            .add_signature_with_metadata(code_unit.clone(), metadata);
3981        self.parsed.add_child(parent.clone(), code_unit);
3982    }
3983
3984    fn visit_recovered_macro_qualified_constructor_definition(
3985        &mut self,
3986        declaration_node: Node<'_>,
3987        call: Node<'_>,
3988        scope: &ScopeInfo,
3989    ) {
3990        let Some(parent) = &scope.class_unit else {
3991            return;
3992        };
3993        let Some(arguments) = call.child_by_field_name("arguments") else {
3994            return;
3995        };
3996        let Some((mut signature, parameter_labels)) =
3997            recovered_macro_qualified_function_parameters(arguments, self.source)
3998        else {
3999            return;
4000        };
4001        if let Some(template_signature) = &scope.template_signature {
4002            signature = format!("{template_signature}{signature}");
4003        }
4004        let arity = parameter_labels.len();
4005        let function = FunctionInfo {
4006            package_name: scope.package_name.clone(),
4007            owner: Some(CppMemberOwner::Unit(parent.clone())),
4008            name: parent.identifier().to_string(),
4009            signature,
4010        };
4011        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4012        self.parsed
4013            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4014        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4015        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4016            .with_declaration_only(false)
4017            .with_callable_arity(CallableArity::exact(arity))
4018            .with_callable_linkage(cpp_callable_linkage(declaration_node, self.source));
4019        self.parsed
4020            .add_signature_with_metadata(code_unit.clone(), metadata);
4021        self.parsed.add_child(parent.clone(), code_unit);
4022    }
4023
4024    fn visit_variable_declaration(
4025        &mut self,
4026        declaration_node: Node<'_>,
4027        declarator: Node<'_>,
4028        scope: &ScopeInfo,
4029        in_class_body: bool,
4030    ) {
4031        let Some(name) = extract_variable_name(declarator, self.source) else {
4032            return;
4033        };
4034        let parent = if in_class_body {
4035            let Some(parent) = &scope.class_unit else {
4036                return;
4037            };
4038            Some(parent)
4039        } else {
4040            None
4041        };
4042        let short_name = match parent {
4043            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4044            None => name.clone(),
4045        };
4046        let fq = cpp_leaf_fq(
4047            &scope.package_name,
4048            parent,
4049            &name,
4050            SegmentKind::Member,
4051            SegmentKind::Member,
4052        );
4053        let code_unit = CodeUnit::new_fq(
4054            self.file.clone(),
4055            CodeUnitType::Field,
4056            scope.package_name.clone(),
4057            short_name,
4058            fq,
4059        );
4060        if self.parsed.contains_declaration(&code_unit) {
4061            return;
4062        }
4063        self.parsed
4064            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4065        self.parsed.add_signature_with_metadata(
4066            code_unit.clone(),
4067            SignatureMetadata::new(
4068                render_cpp_field_signature(declaration_node, declarator, self.source),
4069                Vec::new(),
4070            )
4071            .with_cpp_field_linkage(cpp_field_declaration_linkage(declaration_node, self.source)),
4072        );
4073        if let Some(parent) = &scope.class_unit {
4074            self.parsed.add_child(parent.clone(), code_unit);
4075        } else if let Some(module) = &scope.module {
4076            self.parsed.add_child(module.clone(), code_unit);
4077        }
4078    }
4079
4080    fn visit_class_members_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4081        let mut cursor = node.walk();
4082        for child in node.named_children(&mut cursor) {
4083            if child.kind() == "init_declarator"
4084                && let Some(inner) = child.child_by_field_name("declarator")
4085            {
4086                self.visit_variable_declaration(node, inner, scope, true);
4087            } else if matches!(
4088                child.kind(),
4089                "identifier"
4090                    | "field_identifier"
4091                    | "pointer_declarator"
4092                    | "reference_declarator"
4093                    | "array_declarator"
4094                    | "parenthesized_declarator"
4095            ) {
4096                self.visit_variable_declaration(node, child, scope, true);
4097            }
4098        }
4099    }
4100
4101    fn visit_global_variables_from_declaration(&mut self, node: Node<'_>, scope: &ScopeInfo) {
4102        let mut cursor = node.walk();
4103        for child in node.named_children(&mut cursor) {
4104            if child.kind() == "init_declarator"
4105                && let Some(inner) = child.child_by_field_name("declarator")
4106            {
4107                self.visit_variable_declaration(node, inner, scope, false);
4108            } else if matches!(
4109                child.kind(),
4110                "identifier"
4111                    | "field_identifier"
4112                    | "pointer_declarator"
4113                    | "reference_declarator"
4114                    | "array_declarator"
4115                    | "parenthesized_declarator"
4116            ) {
4117                self.visit_variable_declaration(node, child, scope, false);
4118            }
4119        }
4120    }
4121
4122    fn visit_include(&mut self, node: Node<'_>) {
4123        let raw = normalize_cpp_whitespace(node_text(node, self.source));
4124        self.parsed.imports.push(ImportInfo {
4125            raw_snippet: raw,
4126            is_wildcard: false,
4127            is_global: false,
4128            identifier: None,
4129            alias: None,
4130            path: None,
4131            binder_span: None,
4132        });
4133    }
4134
4135    fn visit_type_declaration<'tree>(
4136        &mut self,
4137        node: Node<'tree>,
4138        scope: &ScopeInfo,
4139        stack: &mut Vec<CppWork<'tree>>,
4140    ) {
4141        if let Some(type_node) = node.child_by_field_name("type")
4142            && matches!(
4143                type_node.kind(),
4144                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4145            )
4146        {
4147            self.visit_class_like(type_node, scope, stack);
4148        }
4149
4150        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4151            let range = Range {
4152                start_byte: node.start_byte(),
4153                end_byte: recovered.end_node.end_byte(),
4154                start_line: node.start_position().row + 1,
4155                end_line: recovered.end_node.end_position().row + 1,
4156            };
4157            let signature = self
4158                .source
4159                .get(range.start_byte..range.end_byte)
4160                .map(normalize_cpp_whitespace)
4161                .unwrap_or_default();
4162            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4163            return;
4164        }
4165
4166        let alias_names = match node.kind() {
4167            "alias_declaration" => extract_alias_declaration_name(node, self.source)
4168                .into_iter()
4169                .collect::<Vec<_>>(),
4170            "type_definition" => extract_typedef_alias_names(node, self.source),
4171            _ => Vec::new(),
4172        };
4173        self.add_type_aliases(node, scope, alias_names);
4174    }
4175
4176    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4177        let signature = normalize_cpp_whitespace(node_text(node, self.source));
4178        self.record_type_aliases(
4179            node,
4180            scope,
4181            alias_names,
4182            signature,
4183            cpp_declaration_range(node),
4184        );
4185    }
4186
4187    fn record_type_aliases(
4188        &mut self,
4189        node: Node<'_>,
4190        scope: &ScopeInfo,
4191        alias_names: Vec<String>,
4192        signature: String,
4193        range: Range,
4194    ) {
4195        if signature.is_empty() {
4196            return;
4197        }
4198        let type_name = node
4199            .child_by_field_name("type")
4200            .and_then(|type_node| type_node.child_by_field_name("name"))
4201            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4202        for alias_name in alias_names {
4203            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4204                continue;
4205            }
4206            let short_name = if let Some(parent) = &scope.class_unit {
4207                cpp_join_nested_short(parent.short_name(), &alias_name)
4208            } else {
4209                alias_name.clone()
4210            };
4211            let fq = cpp_leaf_fq(
4212                &scope.package_name,
4213                scope.class_unit.as_ref(),
4214                &alias_name,
4215                SegmentKind::Nested,
4216                SegmentKind::Type,
4217            );
4218            let code_unit = CodeUnit::with_signature_and_fq(
4219                self.file.clone(),
4220                CodeUnitType::Class,
4221                scope.package_name.clone(),
4222                short_name,
4223                Some(signature.clone()),
4224                false,
4225                fq,
4226            );
4227            // Declaration identity does not include the alias signature. Keep
4228            // each physical range so conditional aliases retain their guards.
4229            self.parsed
4230                .add_code_unit_with_range(code_unit.clone(), range, None, None);
4231            self.parsed
4232                .add_signature(code_unit.clone(), signature.clone());
4233            if let Some(metadata) = &scope.template_metadata {
4234                let mut metadata = metadata.clone();
4235                metadata.primary_fq_name = code_unit.fq_name();
4236                self.parsed
4237                    .set_cpp_template_metadata(code_unit.clone(), metadata);
4238            }
4239            if let Some(parent) = &scope.class_unit {
4240                self.parsed.add_child(parent.clone(), code_unit.clone());
4241            } else if let Some(module) = &scope.module {
4242                self.parsed.add_child(module.clone(), code_unit.clone());
4243            }
4244            self.parsed.mark_type_alias(code_unit);
4245        }
4246    }
4247
4248    fn visit_macro(&mut self, node: Node<'_>) {
4249        let Some(name) = extract_macro_name(node, self.source) else {
4250            return;
4251        };
4252        let signature = node_text(node, self.source).trim_end().to_string();
4253        if signature.is_empty() {
4254            return;
4255        }
4256        let fq = cpp_member_fq("", &name);
4257        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4258        if self.parsed.contains_declaration_identity(&code_unit) {
4259            return;
4260        }
4261        self.parsed
4262            .add_code_unit(code_unit.clone(), node, self.source, None, None);
4263        let name_range = node
4264            .child_by_field_name("name")
4265            .map(cpp_declaration_range)
4266            .unwrap_or_else(|| cpp_declaration_range(node));
4267        self.parsed
4268            .record_materialization(MaterializationRecord::GeneratedDeclaration {
4269                site: cpp_declaration_range(node),
4270                argument: name_range,
4271                kind: GenerationKind::PreprocessorDefinition,
4272                unit: code_unit.clone(),
4273            });
4274        self.parsed.add_signature(code_unit, signature);
4275    }
4276}
4277
4278/// Classify a C++ field while its declaration syntax is already available.
4279///
4280/// The persisted result lets later visibility queries avoid reparsing the
4281/// complete source file only to recover linkage.
4282pub fn cpp_field_declaration_linkage(declaration: Node<'_>, source: &str) -> CppFieldLinkage {
4283    let mut current = declaration.parent();
4284    let mut enclosed_by_class = false;
4285    while let Some(node) = current {
4286        if node.kind() == "namespace_definition"
4287            && node
4288                .child_by_field_name("name")
4289                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4290        {
4291            return CppFieldLinkage::Internal;
4292        }
4293        if matches!(
4294            node.kind(),
4295            "class_specifier" | "struct_specifier" | "union_specifier"
4296        ) && node
4297            .child_by_field_name("name")
4298            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4299        {
4300            return CppFieldLinkage::Internal;
4301        }
4302        if matches!(
4303            node.kind(),
4304            "class_specifier" | "struct_specifier" | "union_specifier"
4305        ) {
4306            enclosed_by_class = true;
4307        }
4308        if matches!(node.kind(), "function_definition" | "lambda_expression") {
4309            return CppFieldLinkage::Internal;
4310        }
4311        current = node.parent();
4312    }
4313    if enclosed_by_class {
4314        return CppFieldLinkage::External;
4315    }
4316    let mut cursor = declaration.walk();
4317    let mut has_static = false;
4318    let mut has_extern = false;
4319    let mut has_inline = false;
4320    let mut has_const = false;
4321    let mut has_constexpr = false;
4322    for child in declaration.named_children(&mut cursor) {
4323        let text = normalize_cpp_whitespace(node_text(child, source));
4324        match (child.kind(), text.as_str()) {
4325            ("storage_class_specifier", "static") => has_static = true,
4326            ("storage_class_specifier", "extern") => has_extern = true,
4327            ("storage_class_specifier", "inline") => has_inline = true,
4328            ("storage_class_specifier", "constexpr") => has_constexpr = true,
4329            ("type_qualifier", "const") => has_const = true,
4330            ("type_qualifier", "constexpr") => has_constexpr = true,
4331            _ => {}
4332        }
4333    }
4334    if has_static {
4335        CppFieldLinkage::Internal
4336    } else if has_extern || has_inline {
4337        CppFieldLinkage::External
4338    } else if has_const || has_constexpr {
4339        CppFieldLinkage::InternalUnlessExternalPeer
4340    } else {
4341        CppFieldLinkage::External
4342    }
4343}
4344
4345fn cpp_declaration_range(node: Node<'_>) -> Range {
4346    Range {
4347        start_byte: node.start_byte(),
4348        end_byte: node.end_byte(),
4349        start_line: node.start_position().row + 1,
4350        end_line: node.end_position().row + 1,
4351    }
4352}
4353
4354/// A recovery interval as a [`Range`], for materialization records whose
4355/// window is a byte region rather than one parser node (the sentinel-macro
4356/// region reparses, issue #941/#1657).
4357fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4358    let line_at = |byte: usize| {
4359        source.as_bytes()[..byte]
4360            .iter()
4361            .filter(|&&b| b == b'\n')
4362            .count()
4363            + 1
4364    };
4365    Range {
4366        start_byte,
4367        end_byte,
4368        start_line: line_at(start_byte),
4369        end_line: line_at(end_byte),
4370    }
4371}
4372
4373pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4374    let mut in_block_comment = false;
4375    for line in source.lines() {
4376        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4377        let trimmed = stripped.trim();
4378        if !looks_like_quoted_include_line(trimmed) {
4379            continue;
4380        }
4381
4382        let raw = normalize_cpp_whitespace(trimmed);
4383        // The tree-sitter walk already recorded every `#include` it could see;
4384        // this line scan only recovers the ones a parse error hid, so skip a
4385        // snippet that is already an import binding.
4386        if parsed
4387            .imports
4388            .iter()
4389            .any(|import| import.raw_snippet == raw)
4390        {
4391            continue;
4392        }
4393
4394        parsed.imports.push(ImportInfo {
4395            raw_snippet: raw,
4396            is_wildcard: false,
4397            is_global: false,
4398            identifier: None,
4399            alias: None,
4400            path: None,
4401            binder_span: None,
4402        });
4403    }
4404}
4405
4406fn looks_like_quoted_include_line(line: &str) -> bool {
4407    let Some(rest) = line.trim_start().strip_prefix('#') else {
4408        return false;
4409    };
4410    let Some(rest) = rest.trim_start().strip_prefix("include") else {
4411        return false;
4412    };
4413    rest.trim_start().starts_with('"')
4414}
4415
4416fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4417    let mut raw = Vec::new();
4418    let mut cursor = node.walk();
4419    for child in node.named_children(&mut cursor) {
4420        if child.kind() == "base_class_clause" {
4421            collect_cpp_base_nodes(child, source, &mut raw);
4422        }
4423    }
4424    raw
4425}
4426
4427fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4428    walk_named_tree_preorder(node, false, |child| match child.kind() {
4429        "type_identifier" | "qualified_identifier" | "template_type" => {
4430            let text = normalize_cpp_whitespace(node_text(child, source));
4431            if !text.is_empty() {
4432                raw.push(text);
4433            }
4434            WalkControl::SkipChildren
4435        }
4436        _ => WalkControl::Continue,
4437    });
4438}
4439
4440fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4441    let mut out = String::new();
4442    let chars: Vec<char> = line.chars().collect();
4443    let mut index = 0;
4444    let mut in_string = false;
4445    let mut in_char = false;
4446    let mut escape = false;
4447
4448    while index < chars.len() {
4449        let ch = chars[index];
4450        let next = chars.get(index + 1).copied();
4451
4452        if *in_block_comment {
4453            if ch == '*' && next == Some('/') {
4454                *in_block_comment = false;
4455                index += 2;
4456            } else {
4457                index += 1;
4458            }
4459            continue;
4460        }
4461
4462        if in_string {
4463            out.push(ch);
4464            if escape {
4465                escape = false;
4466            } else if ch == '\\' {
4467                escape = true;
4468            } else if ch == '"' {
4469                in_string = false;
4470            }
4471            index += 1;
4472            continue;
4473        }
4474
4475        if in_char {
4476            out.push(ch);
4477            if escape {
4478                escape = false;
4479            } else if ch == '\\' {
4480                escape = true;
4481            } else if ch == '\'' {
4482                in_char = false;
4483            }
4484            index += 1;
4485            continue;
4486        }
4487
4488        if ch == '/' && next == Some('/') {
4489            break;
4490        }
4491        if ch == '/' && next == Some('*') {
4492            *in_block_comment = true;
4493            index += 2;
4494            continue;
4495        }
4496        if ch == '"' {
4497            in_string = true;
4498            out.push(ch);
4499            index += 1;
4500            continue;
4501        }
4502        if ch == '\'' {
4503            in_char = true;
4504            out.push(ch);
4505            index += 1;
4506            continue;
4507        }
4508
4509        out.push(ch);
4510        index += 1;
4511    }
4512
4513    out
4514}
4515
4516#[derive(Clone)]
4517struct FunctionInfo {
4518    package_name: String,
4519    owner: Option<CppMemberOwner>,
4520    name: String,
4521    signature: String,
4522}
4523
4524/// Owner of a member function, kept structured so a literal `$` inside a
4525/// source-level class name never crosses a join/split boundary: the legacy
4526/// `$`-joined owner string was re-split at fq construction, dropping a leading
4527/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
4528/// and tripping the package/short boundary assert -- the #2140 corruption one
4529/// level up (#2362).
4530#[derive(Clone)]
4531enum CppMemberOwner {
4532    /// Source-level owner class chain from a qualified declarator-id, one
4533    /// class name per component (`Outer::Inner::method` -> `["Outer",
4534    /// "Inner"]`); each component may itself contain a literal `$`.
4535    Chain(Vec<String>),
4536    /// The lexically enclosing or recovered class unit; the member fq extends
4537    /// its fq directly instead of re-splitting its `$`-joined short chain.
4538    Unit(CodeUnit),
4539}
4540
4541impl CppMemberOwner {
4542    /// The legacy `$`-joined owner chain used in the member's short name.
4543    fn short_chain(&self) -> String {
4544        match self {
4545            Self::Chain(chain) => chain.join("$"),
4546            Self::Unit(parent) => parent.short_name().to_string(),
4547        }
4548    }
4549}
4550
4551enum DeclaratorKind<'a> {
4552    Function(Node<'a>),
4553    Variable(Node<'a>),
4554}
4555
4556impl FunctionInfo {
4557    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4558        self.code_unit_with_synthetic(file, false)
4559    }
4560
4561    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4562        let short_name = match &self.owner {
4563            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
4564            None => self.name.clone(),
4565        };
4566        let fq = match &self.owner {
4567            Some(CppMemberOwner::Chain(chain)) => {
4568                debug_assert!(
4569                    !chain.is_empty(),
4570                    "an empty owner chain is no owner; producers return None instead"
4571                );
4572                let mut fq = FqName::new();
4573                cpp_push_package(&mut fq, &self.package_name);
4574                let mut first = true;
4575                for component in chain {
4576                    let kind = if first {
4577                        SegmentKind::Type
4578                    } else {
4579                        SegmentKind::Nested
4580                    };
4581                    fq.push(cpp_segment(component, kind));
4582                    first = false;
4583                }
4584                fq.push(cpp_segment(&self.name, SegmentKind::Member));
4585                fq
4586            }
4587            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
4588                .fq()
4589                .clone()
4590                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
4591            // An anonymous parent (empty short chain) contributes no owner
4592            // segment -- the same guard as cpp_join_member_short above.
4593            Some(CppMemberOwner::Unit(_)) | None => {
4594                let mut fq = FqName::new();
4595                cpp_push_package(&mut fq, &self.package_name);
4596                fq.push(cpp_segment(&self.name, SegmentKind::Member));
4597                fq
4598            }
4599        };
4600        CodeUnit::with_signature_and_fq(
4601            file,
4602            CodeUnitType::Function,
4603            self.package_name.clone(),
4604            short_name,
4605            Some(self.signature.clone()),
4606            synthetic,
4607            fq,
4608        )
4609    }
4610}
4611
4612fn extract_function_info(
4613    declarator: Node<'_>,
4614    source: &str,
4615    scope: &ScopeInfo,
4616) -> Option<FunctionInfo> {
4617    let parameters_node = declarator.child_by_field_name("parameters")?;
4618    let declarator_name_node = declarator
4619        .child_by_field_name("declarator")
4620        .or_else(|| parameters_node.prev_named_sibling())?;
4621    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4622}
4623
4624fn extract_function_info_from_name(
4625    declarator: Node<'_>,
4626    declarator_name_node: Node<'_>,
4627    source: &str,
4628    scope: &ScopeInfo,
4629) -> Option<FunctionInfo> {
4630    let parameters_node = declarator.child_by_field_name("parameters")?;
4631    let parameters_text = cpp_parameter_signature(parameters_node, source);
4632    let recovered_specialization_member = scope
4633        .recovered_specialization_member_scope
4634        .then(|| {
4635            let terminal = declarator_name_node
4636                .child_by_field_name("name")
4637                .unwrap_or(declarator_name_node);
4638            let name = canonical_cpp_qualified_component(terminal, source)?.name;
4639            let owner = scope.class_unit.as_ref()?;
4640            Some((
4641                Some(CppMemberOwner::Unit(owner.clone())),
4642                name,
4643                scope.package_name.clone(),
4644            ))
4645        })
4646        .flatten();
4647    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
4648        parts
4649    } else if let Some(parts) =
4650        split_structured_templated_cpp_name(declarator_name_node, source, scope)
4651    {
4652        parts
4653    } else {
4654        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4655            declarator_name_node,
4656            source,
4657        )?);
4658        if raw_name.is_empty() {
4659            return None;
4660        }
4661        split_cpp_name(&raw_name, scope)
4662    };
4663    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4664    let mut signature = if suffix.is_empty() {
4665        parameters_text
4666    } else {
4667        format!("{parameters_text} {suffix}")
4668    };
4669    if let Some(template_signature) = &scope.template_signature {
4670        signature = format!("{template_signature}{signature}");
4671    }
4672
4673    Some(FunctionInfo {
4674        package_name,
4675        owner,
4676        name,
4677        signature,
4678    })
4679}
4680
4681/// Recover the semantic return type and callable name when a declaration macro
4682/// occupies a function definition's `type` field. Tree-sitter either exposes a
4683/// scalar return as the declarator's apparent name and the callable as the sole
4684/// identifier in an `ERROR`, or joins a template return and callable into a
4685/// qualified identifier with a missing `::`. Both shapes retain the complete
4686/// parameter list and body; a concrete separator remains an out-of-line member.
4687fn cpp_macro_displaced_callable_parts<'tree>(
4688    function_declarator: Node<'tree>,
4689    source: &str,
4690) -> Option<(Node<'tree>, Node<'tree>)> {
4691    let definition = function_declarator.parent()?;
4692    if definition.kind() != "function_definition"
4693        || definition.child_by_field_name("declarator") != Some(function_declarator)
4694        || definition
4695            .child_by_field_name("body")
4696            .is_none_or(|body| body.kind() != "compound_statement")
4697    {
4698        return None;
4699    }
4700    let macro_type = definition.child_by_field_name("type")?;
4701    if macro_type.kind() != "type_identifier"
4702        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4703    {
4704        return None;
4705    }
4706
4707    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4708    if apparent_return_type.kind() == "qualified_identifier"
4709        && let (Some(return_type), Some(callable_name)) = (
4710            apparent_return_type.child_by_field_name("scope"),
4711            apparent_return_type.child_by_field_name("name"),
4712        )
4713        && return_type.kind() == "template_type"
4714        && matches!(callable_name.kind(), "identifier" | "field_identifier")
4715        && (0..apparent_return_type.child_count())
4716            .filter_map(|index| apparent_return_type.child(index))
4717            .any(|child| child.kind() == "::" && child.is_missing())
4718        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4719        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4720    {
4721        return Some((return_type, callable_name));
4722    }
4723    if !matches!(
4724        apparent_return_type.kind(),
4725        "identifier" | "field_identifier" | "type_identifier"
4726    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
4727    {
4728        return None;
4729    }
4730    let parameters = function_declarator.child_by_field_name("parameters")?;
4731    let mut cursor = function_declarator.walk();
4732    let between = function_declarator
4733        .named_children(&mut cursor)
4734        .filter(|child| child.kind() != "comment")
4735        .filter(|child| {
4736            child.start_byte() >= apparent_return_type.end_byte()
4737                && child.end_byte() <= parameters.start_byte()
4738                && !same_node(*child, apparent_return_type)
4739                && !same_node(*child, parameters)
4740        })
4741        .collect::<Vec<_>>();
4742    let [name_error] = between.as_slice() else {
4743        return None;
4744    };
4745    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
4746        return None;
4747    }
4748    let callable_name = name_error.named_child(0)?;
4749    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
4750        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4751    {
4752        return None;
4753    }
4754    Some((apparent_return_type, callable_name))
4755}
4756
4757/// The part of a `function_declarator` after its parameter list that belongs to
4758/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
4759/// specification, a trailing return type and a trailing requires-clause.
4760///
4761/// The grammar makes each of these a distinct sibling of the `parameters`
4762/// field, so they are read from the tree. Splitting the declarator's text on
4763/// the parameter list instead silently dropped every qualifier whenever the
4764/// parameter list was spelled with whitespace that normalization rewrote - a
4765/// line break or a double space was enough to make a `const` member definition
4766/// a different logical symbol from its declaration (#1827).
4767///
4768/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
4769/// are deliberately excluded. C++ does not make them part of the signature and
4770/// an out-of-line definition never repeats them, so including them would split
4771/// a declaration from its own definition.
4772fn cpp_declarator_identity_suffix(
4773    declarator: Node<'_>,
4774    parameters_node: Node<'_>,
4775    source: &str,
4776) -> String {
4777    let mut cursor = declarator.walk();
4778    let parts = declarator
4779        .named_children(&mut cursor)
4780        .filter(|child| child.start_byte() >= parameters_node.end_byte())
4781        .filter(|child| {
4782            matches!(
4783                child.kind(),
4784                "type_qualifier"
4785                    | "ref_qualifier"
4786                    | "noexcept"
4787                    | "throw_specifier"
4788                    | "trailing_return_type"
4789                    | "requires_clause"
4790            )
4791        })
4792        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
4793        .filter(|text| !text.is_empty())
4794        .collect::<Vec<_>>();
4795    normalize_cpp_qualifier_suffix(&parts.join(" "))
4796}
4797
4798/// The identity suffix of one callable declarator, for a consumer that holds
4799/// the declarator rather than the declaration walk's parts.
4800///
4801/// The persisted signature concatenates the parameter spelling and this suffix,
4802/// so a comparison that must agree on the suffix alone recomputes it here
4803/// instead of splitting the stored string.
4804pub(crate) fn cpp_callable_identity_suffix(
4805    function_declarator: Node<'_>,
4806    source: &str,
4807) -> Option<String> {
4808    let parameters_node = function_declarator.child_by_field_name("parameters")?;
4809    Some(cpp_declarator_identity_suffix(
4810        function_declarator,
4811        parameters_node,
4812        source,
4813    ))
4814}
4815
4816fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
4817    match classify_declarator(node)? {
4818        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
4819        DeclaratorKind::Variable(_) => None,
4820    }
4821}
4822
4823fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
4824    match node.kind() {
4825        "function_declarator" => {
4826            let inner = node
4827                .child_by_field_name("declarator")
4828                .or_else(|| node.child_by_field_name("name"))
4829                .or_else(|| last_named_child(node));
4830            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
4831                Some(DeclaratorKind::Variable(node))
4832            } else {
4833                Some(DeclaratorKind::Function(node))
4834            }
4835        }
4836        "init_declarator"
4837        | "pointer_declarator"
4838        | "reference_declarator"
4839        | "parenthesized_declarator"
4840        | "array_declarator"
4841        | "attributed_declarator"
4842        | "template_function" => node
4843            .child_by_field_name("declarator")
4844            .or_else(|| node.child_by_field_name("name"))
4845            .or_else(|| last_named_child(node))
4846            .and_then(classify_declarator),
4847        "identifier" | "field_identifier" | "qualified_identifier" => {
4848            Some(DeclaratorKind::Variable(node))
4849        }
4850        _ => node
4851            .child_by_field_name("declarator")
4852            .or_else(|| node.child_by_field_name("name"))
4853            .or_else(|| last_named_child(node))
4854            .and_then(classify_declarator),
4855    }
4856}
4857
4858fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
4859    matches!(
4860        node.kind(),
4861        "function_declarator"
4862            | "init_declarator"
4863            | "pointer_declarator"
4864            | "reference_declarator"
4865            | "parenthesized_declarator"
4866            | "array_declarator"
4867            | "attributed_declarator"
4868            | "template_function"
4869            | "identifier"
4870            | "field_identifier"
4871            | "qualified_identifier"
4872    )
4873}
4874
4875fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
4876    let class_like = first_class_like_child(node);
4877    let mut cursor = node.walk();
4878    node.named_children(&mut cursor).any(|child| {
4879        matches!(
4880            child.kind(),
4881            "init_declarator"
4882                | "pointer_declarator"
4883                | "reference_declarator"
4884                | "array_declarator"
4885                | "function_declarator"
4886                | "parenthesized_declarator"
4887                | "attributed_declarator"
4888        ) || matches!(
4889            child.kind(),
4890            "identifier" | "field_identifier" | "qualified_identifier"
4891        ) && class_like.is_none_or(|class_node| {
4892            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
4893        })
4894    })
4895}
4896
4897/// Find the unique namespace-scope forward declaration that precedes a
4898/// recovered export-macro class definition.  Tree-sitter can close a malformed
4899/// class at the enclosing namespace's closing brace, leaving the later class
4900/// definitions as root-level recovered `function_definition` nodes.  A
4901/// preceding `class Name;` in the same namespace is the only structured identity
4902/// signal available in that shape.
4903///
4904/// The search is deliberately conservative: it only accepts a body-less class
4905/// specifier whose declaration has no declarator and is not nested in a function
4906/// or class body.  More than one matching namespace forward declaration is
4907/// ambiguous and returns `None` rather than guessing.
4908fn unique_earlier_cpp_namespace_forward(
4909    recovered_node: Node<'_>,
4910    name: &str,
4911    source: &str,
4912) -> Option<String> {
4913    let mut root = recovered_node;
4914    while let Some(parent) = root.parent() {
4915        root = parent;
4916    }
4917
4918    let mut candidates = Vec::new();
4919    let mut stack = vec![root];
4920    while let Some(current) = stack.pop() {
4921        if current.start_byte() < recovered_node.start_byte()
4922            && matches!(
4923                current.kind(),
4924                "class_specifier" | "struct_specifier" | "union_specifier"
4925            )
4926            && cpp_body_node(current).is_none()
4927            && current.parent().is_some_and(|parent| {
4928                parent.kind() == "declaration_list"
4929                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
4930            })
4931            && class_like_name(current, source).as_deref() == Some(name)
4932            && cpp_namespace_definition_for_forward(current).is_some_and(|namespace| {
4933                // Borrowing is only justified by the parser-recovery shape we
4934                // are repairing: the namespace that held the forward must
4935                // itself contain a syntax error and must have closed before
4936                // the root-level recovered class. A clean, unrelated
4937                // namespace forward is not an identity proof.
4938                namespace.has_error()
4939                    && namespace.end_byte() < recovered_node.start_byte()
4940                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
4941            })
4942            && let Some(package_name) = cpp_namespace_name_for_forward(current, source)
4943        {
4944            candidates.push(package_name);
4945        }
4946
4947        let mut cursor = current.walk();
4948        for child in current.named_children(&mut cursor) {
4949            if child.start_byte() < recovered_node.start_byte() {
4950                stack.push(child);
4951            }
4952        }
4953    }
4954
4955    if candidates.len() == 1 {
4956        candidates.pop()
4957    } else {
4958        None
4959    }
4960}
4961
4962fn malformed_namespace_is_nearest_recovery_region(
4963    namespace: Node<'_>,
4964    recovered_node: Node<'_>,
4965) -> bool {
4966    let mut root = recovered_node;
4967    while let Some(parent) = root.parent() {
4968        root = parent;
4969    }
4970    let mut cursor = root.walk();
4971    root.named_children(&mut cursor)
4972        .filter(|sibling| {
4973            namespace.end_byte() <= sibling.start_byte()
4974                && sibling.end_byte() <= recovered_node.start_byte()
4975        })
4976        .all(is_malformed_namespace_recovery_trivia)
4977}
4978
4979fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
4980    matches!(node.kind(), "ERROR" | "comment")
4981        || node.kind().starts_with("preproc_")
4982        || node.kind() == "expression_statement" && node.named_child_count() == 0
4983}
4984
4985/// Return the namespace path for a forward class only when the declaration is
4986/// at namespace scope.  A declaration nested in a function/class body may share
4987/// the same namespace ancestor but cannot identify a top-level class definition.
4988fn cpp_namespace_name_for_forward(node: Node<'_>, source: &str) -> Option<String> {
4989    cpp_namespace_definition_for_forward(node)?;
4990    cpp_lexical_namespace_name(node, source)
4991}
4992
4993fn cpp_namespace_definition_for_forward(node: Node<'_>) -> Option<Node<'_>> {
4994    let declaration = node.parent()?;
4995    let mut ancestor = declaration.parent();
4996    while let Some(current) = ancestor {
4997        if matches!(
4998            current.kind(),
4999            "compound_statement"
5000                | "field_declaration_list"
5001                | "class_specifier"
5002                | "struct_specifier"
5003                | "union_specifier"
5004                | "function_definition"
5005                | "lambda_expression"
5006        ) {
5007            return None;
5008        }
5009        if current.kind() == "namespace_definition" {
5010            return Some(current);
5011        }
5012        ancestor = current.parent();
5013    }
5014    None
5015}
5016
5017fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5018    match node.kind() {
5019        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5020        "parenthesized_declarator" => node
5021            .child_by_field_name("declarator")
5022            .or_else(|| last_named_child(node))
5023            .is_some_and(is_pointer_wrapper_declarator),
5024        "template_function" => node
5025            .child_by_field_name("name")
5026            .is_some_and(is_function_pointer_like_inner_declarator),
5027        _ => false,
5028    }
5029}
5030
5031fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5032    match node.kind() {
5033        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5034        "parenthesized_declarator" => node
5035            .child_by_field_name("declarator")
5036            .or_else(|| last_named_child(node))
5037            .is_some_and(is_pointer_wrapper_declarator),
5038        _ => false,
5039    }
5040}
5041
5042fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5043    let cleaned = raw_name.trim_start_matches("template ").trim();
5044    // A leading `::` is the explicit-global marker, not an empty owner segment.
5045    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
5046    // erroneous macro envelope swallowing the first identifier of an
5047    // out-of-line `X::X` constructor, chromium #1573); without this strip the
5048    // split below yields owner_parts `[""]`, constructing a unit with an empty
5049    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
5050    let cleaned = cleaned.trim_start_matches("::");
5051    // Parser recovery can preserve two adjacent scope operators around a
5052    // missing component (for example `X::/**/::method` in compiler diagnostic
5053    // fixtures). Empty components are syntax-recovery artifacts, never C++
5054    // owners. Keeping one as the final owner constructed `short_name=".method"`
5055    // and violated the structured package/short boundary during a large LLVM
5056    // workspace build. This is the same legacy-string-to-FqName bridge as the
5057    // ordinary split above; discard only components that the delimiter itself
5058    // proves empty.
5059    let parts: Vec<_> = cleaned
5060        .split("::")
5061        .filter(|component| !component.is_empty())
5062        .collect();
5063    if parts.is_empty() {
5064        return (None, cleaned.to_string(), scope.package_name.clone());
5065    }
5066    if parts.len() > 1 {
5067        let name = parts.last().unwrap_or(&cleaned).to_string();
5068        let owner_parts = &parts[..parts.len() - 1];
5069        if let Some(class_unit) = &scope.class_unit {
5070            // Lexically inside a class body: the owner is that class, whatever
5071            // the declarator re-qualifies it as.
5072            return (
5073                Some(CppMemberOwner::Unit(class_unit.clone())),
5074                name,
5075                scope.package_name.clone(),
5076            );
5077        }
5078        if !scope.package_name.is_empty() {
5079            // Out-of-line member definition written *inside* an enclosing
5080            // `namespace {}` block (scope package is that namespace). Every
5081            // owner segment before the terminal member is a class-nesting step
5082            // -- an out-of-line nested-class member `Outer::Inner::method` in
5083            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
5084            // namespace path: `using namespace` never brings nested-class
5085            // access into unqualified scope, so C++ always writes the full
5086            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
5087            // that redundantly re-states the enclosing namespace it already
5088            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
5089            // strip that re-qualifying prefix (which duplicates a suffix of the
5090            // enclosing package path) before treating what remains as the
5091            // nested-class chain, so the redundant spelling still lands on the
5092            // same `log4cxx.Foo.method` identity as its header declaration.
5093            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
5094            let owner = (!nested.is_empty()).then(|| {
5095                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
5096            });
5097            return (owner, name, scope.package_name.clone());
5098        }
5099        // File scope (no enclosing `namespace {}` block, scope package empty).
5100        let (owner, package_name) = if owner_parts.len() > 1 {
5101            // A multi-segment qualifier at file scope with no enclosing
5102            // namespace: treat all but the last owner segment as the namespace
5103            // path and the last as the owning class (`ns1::ns2::Class::method`
5104            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
5105            // is really a namespace or an outer class cannot be told from the
5106            // declarator text alone here, and no enclosing namespace or
5107            // in-index owner is available at per-file extraction to confirm the
5108            // class reading, so the far-more-common namespace interpretation is
5109            // kept rather than guessed away (the nested-class-at-file-scope and
5110            // using-directive-qualified nested-class shapes remain on this
5111            // behavior; see #1121).
5112            (
5113                Some(CppMemberOwner::Chain(vec![
5114                    owner_parts.last().unwrap_or(&"").to_string(),
5115                ])),
5116                owner_parts[..owner_parts.len() - 1].join("::"),
5117            )
5118        } else {
5119            // A bare `Class::member` qualifier at file scope carries no
5120            // namespace segment of its own. The declarator alone cannot say
5121            // which namespace owns `Class` -- but a `using namespace X;`
5122            // directive already in effect at this point in the file (#1093,
5123            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
5124            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
5125            // is the remaining structural signal for it, so fall back to it
5126            // rather than leaving the definition's package empty while its
5127            // header declaration (parsed inside the `namespace {}` block) keeps
5128            // the real one -- an identity split that made the same member
5129            // unresolvable under its own displayed spelling.
5130            (
5131                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
5132                cpp_using_directive_namespace_for_bare_owner(scope),
5133            )
5134        };
5135        return (owner, name, package_name);
5136    }
5137
5138    let package_name = scope.package_name.clone();
5139    let owner = scope
5140        .class_unit
5141        .as_ref()
5142        .map(|parent| CppMemberOwner::Unit(parent.clone()));
5143    (owner, cleaned.to_string(), package_name)
5144}
5145
5146/// Drop the leading owner segments of an out-of-line member qualifier that
5147/// merely re-state the enclosing namespace the definition already sits in, so
5148/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
5149/// definition may redundantly write `a::b::Outer::Inner::method` (or the
5150/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
5151/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
5152/// noise, not class-nesting steps. Returns the owner segments with the longest
5153/// such re-qualifying prefix removed (possibly all of them, when the qualifier
5154/// names only the enclosing namespace before the terminal member -- a
5155/// re-qualified free function). `package_name` is the enclosing namespace path
5156/// in its stored `::`-joined form; both sides are split on the same delimiter
5157/// the namespace walker joined them with, so this compares namespace *segments*
5158/// rather than scanning text.
5159fn strip_redundant_namespace_prefix<'a>(
5160    owner_parts: &'a [&'a str],
5161    package_name: &str,
5162) -> &'a [&'a str] {
5163    if package_name.is_empty() {
5164        return owner_parts;
5165    }
5166    let package_segments: Vec<&str> = package_name.split("::").collect();
5167    let max_prefix = owner_parts.len().min(package_segments.len());
5168    for prefix_len in (1..=max_prefix).rev() {
5169        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5170        if &owner_parts[..prefix_len] == package_suffix {
5171            return &owner_parts[prefix_len..];
5172        }
5173    }
5174    owner_parts
5175}
5176
5177/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
5178/// class name at file/namespace scope, from the `using namespace` directives
5179/// visible at this point in the file. Several may be in scope at once (a
5180/// primary `using namespace NS;` alongside deeper conveniences like `using
5181/// namespace NS::helpers;`); since the declarator gives no way to tell which
5182/// one actually declares the owner class, prefer the shallowest (fewest
5183/// `::`-separated segments) as the file's most likely "home" namespace,
5184/// breaking ties by declaration order. Returns an empty string (leaving the
5185/// caller's package unqualified, as before) when no using-namespace directive
5186/// is in scope.
5187fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5188    scope
5189        .visible_using_namespaces
5190        .iter()
5191        .min_by_key(|namespace| namespace.split("::").count())
5192        .cloned()
5193        .unwrap_or_default()
5194}
5195
5196struct CppQualifiedNameComponent {
5197    name: String,
5198    is_template_id: bool,
5199}
5200
5201/// Canonical nested-class chain for an out-of-line class definition written
5202/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
5203/// component per class (`["Outer", "Inner"]`).
5204///
5205/// The enclosing namespace fixes the namespace/class boundary: after an
5206/// optional redundant spelling of that namespace, every component belongs to
5207/// the class chain. File-scope qualified class names remain untouched because
5208/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
5209///
5210/// The components stay structured (rather than being `$`-joined here) so the
5211/// fq construction can push one Type/Nested segment per class; the `$`-joined
5212/// short-name display form is derived at the call sites that need it.
5213fn qualified_class_name_chain(
5214    class_node: Node<'_>,
5215    source: &str,
5216    scope: &ScopeInfo,
5217) -> Option<Vec<String>> {
5218    if scope.package_name.is_empty() || scope.class_unit.is_some() {
5219        return None;
5220    }
5221    let name = class_node.child_by_field_name("name")?;
5222    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5223    if explicitly_global
5224        || components.len() < 2
5225        || components.iter().any(|component| component.is_template_id)
5226    {
5227        return None;
5228    }
5229    let names = components
5230        .iter()
5231        .map(|component| component.name.as_str())
5232        .collect::<Vec<_>>();
5233    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5234    if class_chain.is_empty() {
5235        return None;
5236    }
5237    Some(class_chain.iter().map(|name| name.to_string()).collect())
5238}
5239
5240fn structured_cpp_qualified_components(
5241    qualified_name: Node<'_>,
5242    source: &str,
5243) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5244    if qualified_name.kind() != "qualified_identifier" {
5245        return None;
5246    }
5247
5248    let mut components = Vec::new();
5249    let mut current = qualified_name;
5250    let mut explicitly_global = false;
5251    loop {
5252        if current.kind() == "qualified_identifier" {
5253            if let Some(component) = current.child_by_field_name("scope") {
5254                components.push(canonical_cpp_qualified_component(component, source)?);
5255            } else if components.is_empty() {
5256                explicitly_global = true;
5257            } else {
5258                return None;
5259            }
5260            current = current.child_by_field_name("name")?;
5261        } else {
5262            components.push(canonical_cpp_qualified_component(current, source)?);
5263            break;
5264        }
5265    }
5266    Some((components, explicitly_global))
5267}
5268
5269fn split_structured_templated_cpp_name(
5270    declarator_name: Node<'_>,
5271    source: &str,
5272    scope: &ScopeInfo,
5273) -> Option<(Option<CppMemberOwner>, String, String)> {
5274    let (mut components, explicitly_global) =
5275        structured_cpp_qualified_components(declarator_name, source)?;
5276
5277    let terminal = components.pop()?;
5278    let owner_start = components
5279        .iter()
5280        .position(|component| component.is_template_id)?;
5281    let explicit_package = components[..owner_start]
5282        .iter()
5283        .map(|component| component.name.as_str())
5284        .collect::<Vec<_>>()
5285        .join("::");
5286    let explicit_package_is_empty = explicit_package.is_empty();
5287    let package_name = match (
5288        explicitly_global,
5289        scope.package_name.is_empty(),
5290        explicit_package_is_empty,
5291    ) {
5292        (true, _, _) => explicit_package,
5293        (false, _, true) => scope.package_name.clone(),
5294        (false, true, false) => explicit_package,
5295        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5296    };
5297    // Same identity-split fallback as `split_cpp_name` (#1093): a template
5298    // specialization's owner class named with no namespace segment of its own
5299    // (`explicit_package` empty) at file scope (`explicitly_global` false)
5300    // with nothing enclosing (`package_name` still empty) has no structural
5301    // signal for its namespace besides an in-scope `using namespace X;`.
5302    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5303    {
5304        cpp_using_directive_namespace_for_bare_owner(scope)
5305    } else {
5306        package_name
5307    };
5308    let owner_chain = components[owner_start..]
5309        .iter()
5310        .map(|component| component.name.clone())
5311        .collect::<Vec<_>>();
5312    if owner_chain.is_empty() || terminal.name.is_empty() {
5313        return None;
5314    }
5315
5316    Some((
5317        Some(CppMemberOwner::Chain(owner_chain)),
5318        terminal.name,
5319        package_name,
5320    ))
5321}
5322
5323fn canonical_cpp_qualified_component(
5324    mut component: Node<'_>,
5325    source: &str,
5326) -> Option<CppQualifiedNameComponent> {
5327    let mut is_template_id = false;
5328    loop {
5329        match component.kind() {
5330            "template_type" => {
5331                is_template_id = true;
5332                component = component.child_by_field_name("name")?;
5333            }
5334            "dependent_name" => component = component.named_child(0)?,
5335            "identifier"
5336            | "field_identifier"
5337            | "namespace_identifier"
5338            | "type_identifier"
5339            | "operator_name"
5340            | "destructor_name" => {
5341                let name = normalize_cpp_whitespace(node_text(component, source));
5342                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5343                    name,
5344                    is_template_id,
5345                });
5346            }
5347            _ => component = component.child_by_field_name("name")?,
5348        }
5349    }
5350}
5351
5352fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5353    match node.kind() {
5354        "identifier"
5355        | "field_identifier"
5356        | "type_identifier"
5357        | "operator_name"
5358        | "destructor_name"
5359        | "qualified_identifier" => node_text(node, source).to_string(),
5360        "function_declarator"
5361        | "pointer_declarator"
5362        | "reference_declarator"
5363        | "parenthesized_declarator"
5364        | "array_declarator"
5365        | "template_function" => node
5366            .child_by_field_name("declarator")
5367            .or_else(|| node.child_by_field_name("name"))
5368            .or_else(|| last_named_child(node))
5369            .map(|child| extract_declarator_name(child, source))
5370            .unwrap_or_else(|| node_text(node, source).to_string()),
5371        _ => node
5372            .child_by_field_name("name")
5373            .map(|child| extract_declarator_name(child, source))
5374            .unwrap_or_else(|| node_text(node, source).to_string()),
5375    }
5376}
5377
5378/// Extract a callable identity only through declaration-shaped AST nodes.
5379/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
5380/// expose the call's parameter list as a false function declarator; accepting
5381/// arbitrary node text there emitted bogus names such as `.*f`.
5382fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5383    match node.kind() {
5384        "identifier"
5385        | "field_identifier"
5386        | "type_identifier"
5387        | "operator_name"
5388        | "destructor_name"
5389        | "qualified_identifier" => Some(node_text(node, source).to_string()),
5390        "function_declarator"
5391        | "pointer_declarator"
5392        | "reference_declarator"
5393        | "parenthesized_declarator"
5394        | "array_declarator"
5395        | "template_function" => node
5396            .child_by_field_name("declarator")
5397            .or_else(|| node.child_by_field_name("name"))
5398            .and_then(|child| extract_callable_declarator_name(child, source)),
5399        _ => None,
5400    }
5401}
5402
5403fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5404    match node.kind() {
5405        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5406            let name = node_text(node, source).trim().to_string();
5407            (!name.is_empty()).then_some(name)
5408        }
5409        _ => node
5410            .child_by_field_name("declarator")
5411            .or_else(|| node.child_by_field_name("name"))
5412            .or_else(|| last_named_child(node))
5413            .and_then(|child| extract_variable_name(child, source)),
5414    }
5415}
5416
5417fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5418    let count = node.named_child_count();
5419    if count == 0 {
5420        None
5421    } else {
5422        node.named_child(count - 1)
5423    }
5424}
5425
5426fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5427    let name_node = node.child_by_field_name("name")?;
5428    let name = normalize_cpp_whitespace(node_text(name_node, source));
5429    (!name.is_empty()).then_some(name)
5430}
5431
5432fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5433    if node.kind() != "declaration" {
5434        return Vec::new();
5435    }
5436    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5437        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5438    }) else {
5439        return Vec::new();
5440    };
5441    let Some(declarator) = node.child_by_field_name("declarator") else {
5442        return Vec::new();
5443    };
5444    if node_text(keyword, source) == "using"
5445        && (declarator.kind() != "init_declarator"
5446            || declarator.child_by_field_name("value").is_none())
5447    {
5448        return Vec::new();
5449    }
5450    if node_text(keyword, source) == "typedef"
5451        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5452    {
5453        return vec![alias_name];
5454    }
5455    extract_typedef_declarator_name(declarator, source)
5456        .into_iter()
5457        .collect()
5458}
5459
5460fn recovered_typedef_error_alias_name(
5461    declaration: Node<'_>,
5462    declarator: Node<'_>,
5463    source: &str,
5464) -> Option<String> {
5465    // An export macro between `class` and its name can make tree-sitter parse
5466    // the recovered class body as a function body. In that shape,
5467    //
5468    //     typedef spi::Filter BASE_CLASS;
5469    //
5470    // becomes a declaration whose `declarator` is the underlying qualified
5471    // type (`spi::Filter`) and whose actual alias name is displaced into the
5472    // following ERROR node. Do not publish the terminal underlying type
5473    // (`Filter`) as a false class-owned alias.
5474    if declarator.kind() != "qualified_identifier" {
5475        return None;
5476    }
5477    let mut cursor = declaration.walk();
5478    let mut errors = declaration
5479        .named_children(&mut cursor)
5480        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5481    let error = errors.next()?;
5482    if errors.next().is_some() || error.named_child_count() != 1 {
5483        return None;
5484    }
5485    let name = error.named_child(0)?;
5486    if !matches!(
5487        name.kind(),
5488        "identifier" | "field_identifier" | "type_identifier"
5489    ) {
5490        return None;
5491    }
5492    let name = normalize_cpp_whitespace(node_text(name, source));
5493    (!name.is_empty()).then_some(name)
5494}
5495
5496fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5497    // A function-like token in the type position can make tree-sitter expose
5498    // its argument as a parenthesized declarator. Do not publish that argument
5499    // as an alias. The macro-specific recovery below handles the proven shape.
5500    if fragmented_parenthesized_typedef_type(node).is_some() {
5501        return Vec::new();
5502    }
5503    let has_function_like_macro_type = node
5504        .child_by_field_name("type")
5505        .filter(|type_node| type_node.kind() == "type_identifier")
5506        .is_some_and(|type_node| {
5507            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5508        });
5509    let mut names = Vec::new();
5510    let mut cursor = node.walk();
5511    for declarator in node.children_by_field_name("declarator", &mut cursor) {
5512        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5513            continue;
5514        }
5515        if let Some(name) = extract_typedef_declarator_name(declarator, source)
5516            && !names.contains(&name)
5517        {
5518            names.push(name);
5519        }
5520    }
5521    names
5522}
5523
5524struct RecoveredMacroTypedefAlias<'tree> {
5525    name: String,
5526    end_node: Node<'tree>,
5527}
5528
5529/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
5530/// into an identifier expression statement. The uppercase macro token, missing
5531/// typedef terminator, and complete sibling terminator prove this exact shape.
5532fn recovered_macro_typedef_alias<'tree>(
5533    node: Node<'tree>,
5534    source: &str,
5535) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5536    let type_node = fragmented_parenthesized_typedef_type(node)?;
5537    if type_node.kind() != "type_identifier"
5538        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5539    {
5540        return None;
5541    }
5542
5543    let end_node = node.next_named_sibling()?;
5544    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5545        return None;
5546    }
5547    let name_node = end_node.named_child(0)?;
5548    if name_node.kind() != "identifier" {
5549        return None;
5550    }
5551    let has_terminator = (0..end_node.child_count()).any(|index| {
5552        end_node
5553            .child(index)
5554            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5555    });
5556    if !has_terminator {
5557        return None;
5558    }
5559    let name = normalize_cpp_whitespace(node_text(name_node, source));
5560    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5561}
5562
5563fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5564    if node.kind() != "type_definition" {
5565        return None;
5566    }
5567    let mut declarator_cursor = node.walk();
5568    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5569    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5570        return None;
5571    }
5572    let has_missing_terminator = (0..node.child_count()).any(|index| {
5573        node.child(index)
5574            .is_some_and(|child| child.kind() == ";" && child.is_missing())
5575    });
5576    if !has_missing_terminator {
5577        return None;
5578    }
5579    node.child_by_field_name("type")
5580}
5581
5582fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5583    match node.kind() {
5584        "identifier" | "field_identifier" | "type_identifier" => {
5585            let name = normalize_cpp_whitespace(node_text(node, source));
5586            (!name.is_empty()).then_some(name)
5587        }
5588        "qualified_identifier" => node
5589            .child_by_field_name("name")
5590            .and_then(|name| extract_typedef_declarator_name(name, source)),
5591        _ => node
5592            .child_by_field_name("declarator")
5593            .or_else(|| node.child_by_field_name("name"))
5594            .or_else(|| last_named_child(node))
5595            .and_then(|child| extract_typedef_declarator_name(child, source)),
5596    }
5597}
5598
5599fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5600    let name = node
5601        .child_by_field_name("name")
5602        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5603        .or_else(|| {
5604            let mut cursor = node.walk();
5605            node.named_children(&mut cursor)
5606                .find(|child| {
5607                    matches!(
5608                        child.kind(),
5609                        "identifier" | "field_identifier" | "type_identifier"
5610                    )
5611                })
5612                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5613        })?;
5614    (!name.is_empty()).then_some(name)
5615}
5616
5617fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5618    left.id() == right.id()
5619}
5620
5621fn render_cpp_type_signature(
5622    node: Node<'_>,
5623    source: &str,
5624    template_signature: Option<&str>,
5625) -> String {
5626    let text = normalize_cpp_whitespace(node_text(node, source));
5627    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5628    let rendered = if head.ends_with(';') {
5629        head.to_string()
5630    } else {
5631        format!("{head} {{")
5632    };
5633    if let Some(template_signature) = template_signature {
5634        format!("template {template_signature} {rendered}")
5635    } else {
5636        rendered
5637    }
5638}
5639
5640fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5641    if let Some(signature) =
5642        render_recovered_macro_qualified_field_signature(node, declarator, source)
5643    {
5644        return signature;
5645    }
5646    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5647    let prefix = cpp_declaration_prefix(node, source);
5648    let name = extract_variable_name(declarator, source).unwrap_or_default();
5649    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5650    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5651        || (prefix.ends_with('&') && raw_suffix == "&")
5652    {
5653        String::new()
5654    } else {
5655        raw_suffix
5656    };
5657
5658    let mut rendered = if suffix.is_empty() {
5659        format!("{prefix} {name}")
5660    } else if suffix.starts_with('*') || suffix.starts_with('&') {
5661        format!("{prefix}{suffix} {name}")
5662    } else if suffix.starts_with('[') || suffix.starts_with('(') {
5663        format!("{prefix} {name}{suffix}")
5664    } else {
5665        format!("{prefix} {suffix}{name}")
5666    };
5667    rendered = collapse_cpp_whitespace(&rendered);
5668
5669    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5670        format!("{rendered} = {initializer};")
5671    } else if declaration_text.ends_with(';') {
5672        format!("{rendered};")
5673    } else {
5674        rendered
5675    }
5676}
5677
5678fn render_recovered_macro_qualified_field_signature(
5679    node: Node<'_>,
5680    declarator: Node<'_>,
5681    source: &str,
5682) -> Option<String> {
5683    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5684    if !recovered
5685        .iter()
5686        .any(|candidate| same_node(*candidate, declarator))
5687    {
5688        return None;
5689    }
5690    let pseudo_declarator = node.child_by_field_name("declarator")?;
5691    let mut cursor = node.walk();
5692    let clause = node
5693        .named_children(&mut cursor)
5694        .find(|child| child.kind() == "bitfield_clause")?;
5695    let mut cursor = clause.walk();
5696    let error = clause
5697        .named_children(&mut cursor)
5698        .find(|child| child.kind() == "ERROR")?;
5699    let qualified_type =
5700        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5701    let prefix = cpp_declaration_prefix(node, source);
5702    let name = extract_variable_name(declarator, source)?;
5703    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5704    let mut rendered = if suffix.is_empty() {
5705        format!("{prefix} {qualified_type} {name}")
5706    } else {
5707        format!("{prefix} {qualified_type} {suffix} {name}")
5708    };
5709    rendered = collapse_cpp_whitespace(&rendered);
5710
5711    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5712        Some(format!(
5713            "{rendered} = {};",
5714            normalize_cpp_whitespace(node_text(initializer, source))
5715        ))
5716    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5717        Some(format!("{rendered} = {initializer};"))
5718    } else {
5719        Some(format!("{rendered};"))
5720    }
5721}
5722
5723fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
5724    match node.kind() {
5725        "pointer_expression" => {
5726            let operator = node
5727                .child_by_field_name("operator")
5728                .or_else(|| node.child(0))
5729                .map(|operator| node_text(operator, source))
5730                .unwrap_or("*");
5731            let argument = node
5732                .child_by_field_name("argument")
5733                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5734                .unwrap_or_default();
5735            format!("{operator}{argument}")
5736        }
5737        "unary_expression" => {
5738            let operator = node
5739                .child_by_field_name("operator")
5740                .or_else(|| node.child(0))
5741                .map(|operator| node_text(operator, source))
5742                .unwrap_or_default();
5743            let argument = node
5744                .child_by_field_name("argument")
5745                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
5746                .unwrap_or_default();
5747            format!("{operator}{argument}")
5748        }
5749        "identifier" | "field_identifier" => String::new(),
5750        _ => cpp_declarator_suffix_without_name(node, source),
5751    }
5752}
5753
5754fn recovered_macro_qualified_field_initializer<'tree>(
5755    clause: Node<'tree>,
5756    declarator: Node<'tree>,
5757) -> Option<Node<'tree>> {
5758    let mut stack = vec![clause];
5759    while let Some(current) = stack.pop() {
5760        if current.kind() == "assignment_expression"
5761            && current
5762                .child_by_field_name("left")
5763                .is_some_and(|left| same_node(left, declarator))
5764        {
5765            return current.child_by_field_name("right");
5766        }
5767        let mut cursor = current.walk();
5768        stack.extend(current.named_children(&mut cursor));
5769    }
5770    None
5771}
5772
5773fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
5774    let text = node_text(node, source);
5775    let mut cursor = node.walk();
5776    let first_declarator = node.named_children(&mut cursor).find(|child| {
5777        matches!(
5778            child.kind(),
5779            "init_declarator"
5780                | "identifier"
5781                | "field_identifier"
5782                | "pointer_declarator"
5783                | "reference_declarator"
5784                | "array_declarator"
5785                | "function_declarator"
5786        )
5787    });
5788    let prefix = if let Some(first_declarator) = first_declarator {
5789        let end = first_declarator
5790            .start_byte()
5791            .saturating_sub(node.start_byte());
5792        let mut prefix = text.get(..end).unwrap_or(text).to_string();
5793        let declarator_suffix = match first_declarator.kind() {
5794            "init_declarator" => first_declarator
5795                .child_by_field_name("declarator")
5796                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
5797                .unwrap_or_default(),
5798            _ => cpp_declarator_suffix_without_name(first_declarator, source),
5799        };
5800        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
5801            prefix.push_str(&declarator_suffix);
5802        }
5803        return collapse_cpp_whitespace(&prefix)
5804            .trim_end_matches(',')
5805            .trim_end_matches(';')
5806            .trim()
5807            .to_string();
5808    } else {
5809        text
5810    };
5811    collapse_cpp_whitespace(prefix)
5812        .trim_end_matches(',')
5813        .trim_end_matches(';')
5814        .trim()
5815        .to_string()
5816}
5817
5818fn cpp_preserved_initializer(
5819    declaration_node: Node<'_>,
5820    declarator: Node<'_>,
5821    source: &str,
5822) -> Option<String> {
5823    let name = extract_variable_name(declarator, source)?;
5824    let mut cursor = declaration_node.walk();
5825    for child in declaration_node.named_children(&mut cursor) {
5826        if child.kind() != "init_declarator" {
5827            continue;
5828        }
5829        let Some(inner) = child.child_by_field_name("declarator") else {
5830            continue;
5831        };
5832        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
5833            continue;
5834        }
5835        let value = child.child_by_field_name("value")?;
5836        let kind = value.kind();
5837        if matches!(
5838            kind,
5839            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
5840        ) {
5841            return Some(normalize_cpp_whitespace(node_text(value, source)));
5842        }
5843        break;
5844    }
5845    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
5846    let pattern = format!(
5847        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
5848        regex::escape(&name)
5849    );
5850    Regex::new(&pattern)
5851        .ok()
5852        .and_then(|regex| regex.captures(&declaration_text))
5853        .and_then(|captures| captures.get(1))
5854        .map(|value| value.as_str().to_string())
5855}
5856
5857fn render_cpp_function_display_signature_from_node(
5858    node: Node<'_>,
5859    source: &str,
5860    template_signature: Option<&str>,
5861    has_body: bool,
5862) -> String {
5863    let root = enclosing_cpp_declaration_node(node).unwrap_or(node);
5864    let parent_text = node_text(root, source);
5865    let body_local_start = root
5866        .child_by_field_name("body")
5867        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
5868        .unwrap_or(parent_text.len());
5869    let display = parent_text
5870        .get(..body_local_start)
5871        .unwrap_or(parent_text)
5872        .trim()
5873        .trim();
5874    let display = if let Some(template_signature) = template_signature {
5875        if display.starts_with("template ") {
5876            display.to_string()
5877        } else {
5878            format!("template {template_signature} {display}")
5879        }
5880    } else {
5881        display.to_string()
5882    };
5883    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
5884    if has_body {
5885        format!("{display} {{...}}")
5886    } else {
5887        format!("{display};")
5888    }
5889}
5890
5891fn cpp_template_signature(
5892    template_node: Node<'_>,
5893    declaration_child: Node<'_>,
5894    source: &str,
5895) -> Option<String> {
5896    let text = source
5897        .get(template_node.start_byte()..declaration_child.start_byte())
5898        .unwrap_or("");
5899    let text = normalize_cpp_whitespace(text);
5900    let start = text.find('<')?;
5901    let end = text.rfind('>')?;
5902    if end < start {
5903        return None;
5904    }
5905    Some(text[start..=end].to_string())
5906}
5907
5908struct RecoveredFragmentedPartialSpecialization<'tree> {
5909    declaration_node: Node<'tree>,
5910    name: String,
5911    range: Range,
5912    prefix_members: Vec<Node<'tree>>,
5913    member_siblings: Vec<Node<'tree>>,
5914    following_declarations: Vec<Node<'tree>>,
5915}
5916
5917struct RecoveredFragmentedPreprocessorClass<'tree> {
5918    declaration_node: Node<'tree>,
5919    class_node: Node<'tree>,
5920    body: Node<'tree>,
5921    name: String,
5922    range: Range,
5923    tail_members: Vec<Node<'tree>>,
5924    member_siblings: Vec<Node<'tree>>,
5925}
5926
5927/// Recover a class whose preprocessor-fragmented parse closes at an early
5928/// member body and publishes the remaining in-class declarations as siblings
5929/// of the surrounding alternative. Primary classes are admitted only when an
5930/// earlier branch contains the matching bodyless declaration and the class
5931/// node retains the displaced `#endif`. Partial specializations instead carry
5932/// their identity structurally in the `template_type` name and template
5933/// metadata. Retain the original AST nodes and re-own only the siblings through
5934/// the displaced structural `};` terminator.
5935fn recover_fragmented_preprocessor_class<'tree>(
5936    template_node: Node<'tree>,
5937    source: &str,
5938) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
5939    let alternative = template_node.parent()?;
5940    if alternative.kind() != "preproc_else" {
5941        return None;
5942    }
5943    let conditional = alternative.parent()?;
5944    if conditional.kind() != "preproc_if" {
5945        return None;
5946    }
5947    let declaration_node = template_node
5948        .named_children(&mut template_node.walk())
5949        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
5950    let class_node = declaration_node
5951        .named_children(&mut declaration_node.walk())
5952        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
5953    let body = cpp_body_node(class_node)?;
5954    if class_node.end_byte() >= declaration_node.end_byte() {
5955        return None;
5956    }
5957    let name = class_like_name(class_node, source)?;
5958    let is_partial_specialization = class_node
5959        .child_by_field_name("name")
5960        .is_some_and(|class_name| class_name.kind() == "template_type");
5961    if is_partial_specialization {
5962        let metadata = cpp_template_metadata(template_node, class_node, source)?;
5963        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
5964            return None;
5965        }
5966    } else {
5967        if !class_has_displaced_preprocessor_terminator(class_node) {
5968            return None;
5969        }
5970        let matching_other_branch = conditional
5971            .named_children(&mut conditional.walk())
5972            .take_while(|child| !same_node(*child, alternative))
5973            .filter(|child| child.kind() == "template_declaration")
5974            .filter_map(first_class_like_child)
5975            .any(|candidate| {
5976                cpp_body_node(candidate).is_none()
5977                    && class_like_name(candidate, source).as_deref() == Some(name.as_str())
5978            });
5979        if !matching_other_branch {
5980            return None;
5981        }
5982    }
5983
5984    let mut tail_members = Vec::new();
5985    let mut saw_class = false;
5986    let mut declaration_cursor = declaration_node.walk();
5987    for child in declaration_node.named_children(&mut declaration_cursor) {
5988        if same_node(child, class_node) {
5989            saw_class = true;
5990        } else if saw_class {
5991            tail_members.push(child);
5992        }
5993    }
5994
5995    let mut member_siblings = Vec::new();
5996    let mut saw_template = false;
5997    let mut terminator = None;
5998    for index in 0..alternative.child_count() {
5999        let Some(child) = alternative.child(index) else {
6000            continue;
6001        };
6002        if same_node(child, template_node) {
6003            saw_template = true;
6004            continue;
6005        }
6006        if !saw_template {
6007            continue;
6008        }
6009        if displaced_fragmented_class_terminator(alternative, index) {
6010            terminator = alternative.child(index + 1);
6011            break;
6012        }
6013        if child.is_named() {
6014            member_siblings.push(child);
6015        }
6016    }
6017    let terminator = terminator?;
6018    Some(RecoveredFragmentedPreprocessorClass {
6019        declaration_node,
6020        class_node,
6021        body,
6022        name,
6023        range: Range {
6024            start_byte: class_node.start_byte(),
6025            end_byte: terminator.end_byte(),
6026            start_line: class_node.start_position().row + 1,
6027            end_line: terminator.end_position().row + 1,
6028        },
6029        tail_members,
6030        member_siblings,
6031    })
6032}
6033
6034fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6035    (0..class_node.child_count()).any(|index| {
6036        class_node.child(index).is_some_and(|child| {
6037            child.kind() == "ERROR"
6038                && (0..child.child_count()).any(|error_index| {
6039                    child
6040                        .child(error_index)
6041                        .is_some_and(|token| token.kind() == "#endif")
6042                })
6043        })
6044    })
6045}
6046
6047/// The real `#endif` that tree-sitter consumed inside an error subtree.
6048///
6049/// A preprocessor directive inside a malformed array bound can cause later
6050/// declarations to remain children of the conditional. The non-missing token
6051/// still gives the exact structured boundary. Ignore nested conditionals and
6052/// select the last error-owned token. Tree-sitter can pair a later outer
6053/// `#endif` with this conditional, so the direct terminator is not necessarily
6054/// missing.
6055pub fn cpp_displaced_preprocessor_terminator<'tree>(
6056    conditional: Node<'tree>,
6057) -> Option<Node<'tree>> {
6058    if !conditional.has_error() {
6059        return None;
6060    }
6061    let has_concrete_direct_terminator = conditional
6062        .child_count()
6063        .checked_sub(1)
6064        .and_then(|index| conditional.child(index))
6065        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6066    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6067        // A structured alternative proves that the direct `#endif` closes
6068        // this family. An error-owned terminator inside either branch belongs
6069        // to a damaged nested conditional, not to this one.
6070        return None;
6071    }
6072    let mut displaced = None;
6073    let mut stack = (0..conditional.child_count())
6074        .filter_map(|index| conditional.child(index))
6075        .map(|child| (child, false))
6076        .collect::<Vec<_>>();
6077    while let Some((node, inside_error)) = stack.pop() {
6078        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6079            continue;
6080        }
6081        if node.kind() == "#endif" && !node.is_missing() && inside_error {
6082            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6083                displaced = Some(node);
6084            }
6085            continue;
6086        }
6087        if node != conditional
6088            && matches!(
6089                node.kind(),
6090                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
6091            )
6092        {
6093            continue;
6094        }
6095        let inside_error = inside_error || node.kind() == "ERROR";
6096        for index in 0..node.child_count() {
6097            if let Some(child) = node.child(index) {
6098                stack.push((child, inside_error));
6099            }
6100        }
6101    }
6102    displaced
6103}
6104
6105/// The effective end of a conditional whose real terminator tree-sitter
6106/// displaced into declaration recovery.
6107///
6108/// Most damaged conditionals retain a concrete `#endif` token below an
6109/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
6110/// boundary. A preprocessor family that selects the middle of a declaration
6111/// can lose the directive tokens entirely. In that shape tree-sitter leaves
6112/// the declaration's `typedef` token as the sole child of the immediately
6113/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
6114/// declarator name inside the conditional's first declaration. The declaration
6115/// end is then the smallest structured boundary that contains the whole split
6116/// declaration.
6117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6118pub struct CppDisplacedPreprocessorBoundary {
6119    pub end_byte: usize,
6120    pub end_line: usize,
6121}
6122
6123pub fn cpp_displaced_preprocessor_boundary(
6124    conditional: Node<'_>,
6125) -> Option<CppDisplacedPreprocessorBoundary> {
6126    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
6127        return Some(CppDisplacedPreprocessorBoundary {
6128            end_byte: terminator.end_byte(),
6129            end_line: terminator.end_position().row + 1,
6130        });
6131    }
6132    if let Some(declaration) = displaced_split_declaration(conditional) {
6133        return Some(CppDisplacedPreprocessorBoundary {
6134            end_byte: declaration.end_byte(),
6135            end_line: declaration.end_position().row + 1,
6136        });
6137    }
6138    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6139        return Some(CppDisplacedPreprocessorBoundary {
6140            end_byte: terminator.end_byte(),
6141            end_line: terminator.end_position().row + 1,
6142        });
6143    }
6144    None
6145}
6146
6147fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6148    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6149        return None;
6150    }
6151    let mut cursor = conditional.walk();
6152    let declarations = conditional
6153        .named_children(&mut cursor)
6154        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6155        .collect::<Vec<_>>();
6156    let declaration = *declarations.first()?;
6157    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6158        return None;
6159    }
6160    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6161    let mut terminator = None;
6162    let mut stack = (0..declaration.child_count())
6163        .filter_map(|index| declaration.child(index))
6164        .filter(|child| child.start_byte() < declarator_start)
6165        .map(|child| (child, false))
6166        .collect::<Vec<_>>();
6167    while let Some((node, inside_error)) = stack.pop() {
6168        let inside_error = inside_error || node.kind() == "ERROR";
6169        if inside_error && node.kind() == "#endif" && !node.is_missing() {
6170            terminator = Some(node);
6171            continue;
6172        }
6173        for index in 0..node.child_count() {
6174            if let Some(child) = node.child(index)
6175                && child.start_byte() < declarator_start
6176            {
6177                stack.push((child, inside_error));
6178            }
6179        }
6180    }
6181    terminator
6182}
6183
6184fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6185    if !conditional.has_error()
6186        || conditional.child_by_field_name("alternative").is_some()
6187        || conditional
6188            .prev_named_sibling()
6189            .filter(|sibling| {
6190                sibling.kind() == "ERROR"
6191                    && sibling.child_count() == 1
6192                    && sibling
6193                        .child(0)
6194                        .is_some_and(|child| child.kind() == "typedef")
6195            })
6196            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6197            .is_none()
6198    {
6199        return None;
6200    }
6201    let mut cursor = conditional.walk();
6202    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6203    let declaration_index = children
6204        .iter()
6205        .position(|child| child.kind() == "declaration" && child.has_error())?;
6206    let declaration = children[declaration_index];
6207    if !children
6208        .iter()
6209        .skip(declaration_index + 1)
6210        .any(|child| child.end_byte() > declaration.end_byte())
6211    {
6212        return None;
6213    }
6214    let declarator = declaration.child_by_field_name("declarator")?;
6215    let mut error_end = None;
6216    let mut names = Vec::new();
6217    let mut stack = vec![declarator];
6218    while let Some(node) = stack.pop() {
6219        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6220            error_end =
6221                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6222            continue;
6223        }
6224        if matches!(node.kind(), "identifier" | "type_identifier") {
6225            names.push(node.start_byte());
6226        }
6227        for index in (0..node.named_child_count()).rev() {
6228            if let Some(child) = node.named_child(index) {
6229                stack.push(child);
6230            }
6231        }
6232    }
6233    let error_end = error_end?;
6234    names
6235        .into_iter()
6236        .any(|start| start >= error_end)
6237        .then_some(declaration)
6238}
6239
6240fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6241    let Some(error) = parent.child(error_index) else {
6242        return false;
6243    };
6244    if error.kind() != "ERROR"
6245        || error.child_count() != 1
6246        || error.child(0).is_none_or(|child| child.kind() != "}")
6247    {
6248        return false;
6249    }
6250    let Some(semicolon) = parent.child(error_index + 1) else {
6251        return false;
6252    };
6253    semicolon.kind() == "expression_statement"
6254        && semicolon.child_count() == 1
6255        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6256}
6257
6258/// Locate the real end of a class-like declaration when a macro invocation
6259/// without a source semicolon absorbs the class's `};` into its parsed field.
6260/// The grammar then keeps following namespace declarations as later children
6261/// of the same field list. The direct ERROR-plus-semicolon pair proves the
6262/// boundary structurally; no source-text delimiter scan is needed.
6263fn displaced_macro_class_tail(
6264    declaration_node: Node<'_>,
6265    body: Node<'_>,
6266    source: &str,
6267) -> Option<DisplacedMacroClassTail> {
6268    if !matches!(
6269        declaration_node.kind(),
6270        "class_specifier" | "struct_specifier" | "union_specifier"
6271    ) || body.kind() != "field_declaration_list"
6272    {
6273        return None;
6274    }
6275
6276    let child_count = body.named_child_count();
6277    for index in 0..child_count {
6278        let child = body.named_child(index)?;
6279        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6280            continue;
6281        };
6282        let split_index = index + 1;
6283        if split_index >= child_count {
6284            return None;
6285        }
6286        let mut cursor = body.walk();
6287        if !body
6288            .named_children(&mut cursor)
6289            .skip(split_index)
6290            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6291        {
6292            return None;
6293        }
6294        return Some(DisplacedMacroClassTail {
6295            split_index,
6296            class_range: Range {
6297                start_byte: declaration_node.start_byte(),
6298                end_byte: terminator.end_byte(),
6299                start_line: declaration_node.start_position().row + 1,
6300                end_line: terminator.end_position().row + 1,
6301            },
6302        });
6303    }
6304    None
6305}
6306
6307fn displaced_macro_field_terminator<'tree>(
6308    field: Node<'tree>,
6309    source: &str,
6310) -> Option<Node<'tree>> {
6311    if field.kind() != "field_declaration" {
6312        return None;
6313    }
6314    let macro_type = field.child_by_field_name("type")?;
6315    if macro_type.kind() != "type_identifier"
6316        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6317        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6318    {
6319        return None;
6320    }
6321    for index in 0..field.child_count() {
6322        let error = field.child(index)?;
6323        if error.kind() != "ERROR"
6324            || error.child_count() != 1
6325            || error.child(0).is_none_or(|child| child.kind() != "}")
6326        {
6327            continue;
6328        }
6329        let semicolon = field.child(index + 1)?;
6330        if semicolon.kind() == ";" {
6331            return Some(semicolon);
6332        }
6333    }
6334    None
6335}
6336
6337fn recover_fragmented_partial_specialization<'tree>(
6338    template_node: Node<'tree>,
6339    declaration_child: Node<'tree>,
6340    source: &str,
6341) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6342    if declaration_child.kind() != "function_definition" {
6343        return None;
6344    }
6345    let class_node = declaration_child.child_by_field_name("type")?;
6346    if !matches!(
6347        class_node.kind(),
6348        "class_specifier" | "struct_specifier" | "union_specifier"
6349    ) || !class_node
6350        .child_by_field_name("name")
6351        .and_then(|name| direct_identifier_name(name, source))
6352        .is_some_and(|name| cpp_export_macro_token(&name))
6353    {
6354        return None;
6355    }
6356    let declarator = declaration_child.child_by_field_name("declarator")?;
6357    if declarator.kind() != "template_function" {
6358        return None;
6359    }
6360    let metadata = cpp_template_metadata(template_node, declaration_child, source)?;
6361    if metadata.specialization_arguments.is_empty() {
6362        return None;
6363    }
6364    let body = declaration_child.child_by_field_name("body")?;
6365    if body.kind() != "compound_statement" {
6366        return None;
6367    }
6368    let complete_prefix = body.named_child(0).filter(|first| {
6369        first.kind() == "labeled_statement"
6370            && first.has_error()
6371            && first
6372                .named_child(first.named_child_count().saturating_sub(1))
6373                .is_some_and(recovered_declaration_has_class_terminator)
6374    });
6375    let complete_body = complete_prefix.is_some();
6376    let mut prefix_members = Vec::new();
6377    if let Some(prefix) = complete_prefix {
6378        prefix_members.push(prefix);
6379    } else {
6380        let mut body_cursor = body.walk();
6381        for child in body.named_children(&mut body_cursor) {
6382            if !is_structurally_valid_fragmented_class_prefix_member(child) {
6383                break;
6384            }
6385            prefix_members.push(child);
6386        }
6387    }
6388    let containing_declarations = template_node.parent()?;
6389    if !matches!(
6390        containing_declarations.kind(),
6391        "declaration_list" | "compound_statement"
6392    ) {
6393        return None;
6394    }
6395    let mut member_siblings = Vec::new();
6396    let mut following_declarations = Vec::new();
6397    let terminator;
6398    if complete_body {
6399        terminator = complete_prefix?;
6400        let mut cursor = body.walk();
6401        let mut after_prefix = false;
6402        for child in body.named_children(&mut cursor) {
6403            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6404                after_prefix = true;
6405            } else if after_prefix {
6406                following_declarations.push(child);
6407            }
6408        }
6409    } else {
6410        let mut found_template = false;
6411        let mut cursor = containing_declarations.walk();
6412        let mut class_terminator = None;
6413        for child in containing_declarations.children(&mut cursor) {
6414            if same_node(child, template_node) {
6415                found_template = true;
6416                continue;
6417            }
6418            if found_template && child.kind() == "}" {
6419                class_terminator = Some(child);
6420                break;
6421            }
6422            // A namespace can never be a class member: reaching one before the
6423            // terminator proves the class's true close was swallowed upstream
6424            // and this scan has crossed into the enclosing scope, so the
6425            // recovery cannot be bounded -- continuing re-owns the namespace
6426            // block (and its template specializations) as class members under
6427            // a re-appended package, desyncing the fq boundary (#2306).
6428            if found_template && child.kind() == "namespace_definition" {
6429                return None;
6430            }
6431            if found_template && child.is_named() {
6432                member_siblings.push(child);
6433            }
6434        }
6435        terminator = class_terminator?;
6436    }
6437    let name = format!(
6438        "{}<{}>",
6439        metadata.primary_name,
6440        metadata
6441            .specialization_arguments
6442            .iter()
6443            .map(|argument| argument.text.as_str())
6444            .collect::<Vec<_>>()
6445            .join(", ")
6446    );
6447    Some(RecoveredFragmentedPartialSpecialization {
6448        declaration_node: declaration_child,
6449        name,
6450        range: Range {
6451            start_byte: declaration_child.start_byte(),
6452            end_byte: terminator.end_byte(),
6453            start_line: declaration_child.start_position().row + 1,
6454            end_line: terminator.end_position().row + 1,
6455        },
6456        prefix_members,
6457        member_siblings,
6458        following_declarations,
6459    })
6460}
6461
6462fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6463    if declaration.kind() != "declaration" {
6464        return false;
6465    }
6466    // With an export macro between `class` and its name, tree-sitter folds a
6467    // complete class body into a function-shaped declaration. The class's own
6468    // `};` remains structurally identifiable as a direct ERROR child holding
6469    // `}`, immediately followed by the declaration's direct `;` child.
6470    (0..declaration.child_count().saturating_sub(1)).any(|index| {
6471        let Some(error) = declaration.child(index) else {
6472            return false;
6473        };
6474        error.kind() == "ERROR"
6475            && error.child_count() == 1
6476            && error.child(0).is_some_and(|child| child.kind() == "}")
6477            && declaration
6478                .child(index + 1)
6479                .is_some_and(|child| child.kind() == ";")
6480    })
6481}
6482
6483fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6484    if node.has_error() {
6485        return false;
6486    }
6487    match node.kind() {
6488        "declaration"
6489        | "field_declaration"
6490        | "alias_declaration"
6491        | "type_definition"
6492        | "static_assert_declaration" => true,
6493        "labeled_statement" => node
6494            .named_child(node.named_child_count().saturating_sub(1))
6495            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6496        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6497            matches!(
6498                child.kind(),
6499                "declaration"
6500                    | "field_declaration"
6501                    | "alias_declaration"
6502                    | "type_definition"
6503                    | "function_definition"
6504            )
6505        }),
6506        _ => false,
6507    }
6508}
6509
6510fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6511    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6512        .then(|| node.child_by_field_name("declarator"))
6513        .flatten()
6514        .and_then(|declarator| extract_variable_name(declarator, source))
6515}
6516
6517fn cpp_template_metadata(
6518    template_node: Node<'_>,
6519    declaration_child: Node<'_>,
6520    source: &str,
6521) -> Option<CppTemplateMetadata> {
6522    let parameters_node = template_node.child_by_field_name("parameters")?;
6523    let name_node = cpp_templated_class_name_node(declaration_child)?;
6524    let primary_node = match name_node.kind() {
6525        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6526        _ => name_node,
6527    };
6528    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6529    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6530        return None;
6531    }
6532
6533    let mut parameter_nodes = Vec::new();
6534    let mut parameter_names = Vec::new();
6535    let mut cursor = parameters_node.walk();
6536    for parameter in parameters_node.named_children(&mut cursor) {
6537        if !matches!(
6538            parameter.kind(),
6539            "type_parameter_declaration"
6540                | "optional_type_parameter_declaration"
6541                | "variadic_type_parameter_declaration"
6542                | "template_template_parameter_declaration"
6543                | "parameter_declaration"
6544                | "optional_parameter_declaration"
6545                | "variadic_parameter_declaration"
6546        ) {
6547            continue;
6548        }
6549        let index = parameter_nodes.len();
6550        // An unnamed parameter still contributes template arity and kind. Use
6551        // an impossible C++ identifier so positional reconciliation can bind
6552        // it without making source expressions refer to a name that was not
6553        // written.
6554        let name = cpp_template_parameter_name(parameter, source)
6555            .unwrap_or_else(|| format!("<anonymous:{index}>"));
6556        parameter_names.push(name);
6557        parameter_nodes.push(parameter);
6558    }
6559    let parameters = parameter_nodes
6560        .into_iter()
6561        .zip(parameter_names.iter().cloned())
6562        .map(|(parameter, name)| CppTemplateParameterMetadata {
6563            name,
6564            kind: cpp_template_parameter_kind(parameter),
6565            variadic: matches!(
6566                parameter.kind(),
6567                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6568            ),
6569            default: cpp_template_parameter_default_expression(parameter, source, &parameter_names),
6570        })
6571        .collect();
6572    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6573        Vec::new()
6574    } else {
6575        cpp_template_argument_expressions(name_node, source, &parameter_names).unwrap_or_default()
6576    };
6577    let alias_target = (declaration_child.kind() == "alias_declaration")
6578        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names))
6579        .flatten();
6580    Some(CppTemplateMetadata {
6581        primary_name,
6582        primary_fq_name: String::new(),
6583        parameters,
6584        specialization_arguments,
6585        alias_target,
6586    })
6587}
6588
6589fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6590    match node.kind() {
6591        "class_specifier" | "struct_specifier" | "union_specifier" => {
6592            node.child_by_field_name("name")
6593        }
6594        "function_definition" => {
6595            let declarator = node.child_by_field_name("declarator")?;
6596            if matches!(declarator.kind(), "identifier" | "template_function") {
6597                Some(declarator)
6598            } else {
6599                None
6600            }
6601        }
6602        "alias_declaration" => node.child_by_field_name("name"),
6603        _ => None,
6604    }
6605}
6606
6607fn cpp_template_alias_target(
6608    alias: Node<'_>,
6609    source: &str,
6610    parameter_names: &[String],
6611) -> Option<CppTemplateAliasTargetMetadata> {
6612    let mut type_node = alias.child_by_field_name("type")?;
6613    while type_node.kind() == "type_descriptor" {
6614        type_node = type_node.child_by_field_name("type")?;
6615    }
6616    let global = type_node.child_by_field_name("scope").is_none()
6617        && type_node.child(0).is_some_and(|child| child.kind() == "::");
6618    let mut components = Vec::new();
6619    cpp_template_target_components(type_node, source, &mut components)?;
6620    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names);
6621    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6622        components,
6623        global,
6624        arguments,
6625    })
6626}
6627
6628fn cpp_template_target_components(
6629    node: Node<'_>,
6630    source: &str,
6631    out: &mut Vec<String>,
6632) -> Option<()> {
6633    match node.kind() {
6634        "identifier" | "namespace_identifier" | "type_identifier" => {
6635            out.push(node_text(node, source).to_string());
6636            Some(())
6637        }
6638        "template_type" => {
6639            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6640        }
6641        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6642            if let Some(scope) = node.child_by_field_name("scope") {
6643                cpp_template_target_components(scope, source, out)?;
6644            }
6645            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6646        }
6647        _ => None,
6648    }
6649}
6650
6651fn cpp_template_argument_expressions(
6652    mut node: Node<'_>,
6653    source: &str,
6654    parameter_names: &[String],
6655) -> Option<Vec<CppTemplateExpression>> {
6656    loop {
6657        match node.kind() {
6658            "template_type" | "template_function" => {
6659                let arguments = node.child_by_field_name("arguments")?;
6660                let mut cursor = arguments.walk();
6661                return Some(
6662                    arguments
6663                        .named_children(&mut cursor)
6664                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6665                        .map(|argument| cpp_template_expression(argument, source, parameter_names))
6666                        .collect(),
6667                );
6668            }
6669            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6670                node = node
6671                    .child_by_field_name("name")
6672                    .or_else(|| node.child_by_field_name("type"))?;
6673            }
6674            _ => return None,
6675        }
6676    }
6677}
6678
6679fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6680    let candidate = node
6681        .child_by_field_name("name")
6682        .or_else(|| node.child_by_field_name("declarator"))
6683        .or_else(|| {
6684            let mut cursor = node.walk();
6685            node.named_children(&mut cursor).find(|child| {
6686                matches!(
6687                    child.kind(),
6688                    "identifier" | "type_identifier" | "field_identifier"
6689                )
6690            })
6691        })?;
6692    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6693    (!name.is_empty()).then_some(name)
6694}
6695
6696fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6697    match node.kind() {
6698        "type_parameter_declaration"
6699        | "optional_type_parameter_declaration"
6700        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
6701        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
6702        _ => CppTemplateParameterKind::Value,
6703    }
6704}
6705
6706fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
6707    node.child_by_field_name("default_type")
6708        .or_else(|| node.child_by_field_name("default_value"))
6709}
6710
6711fn cpp_template_parameter_default_expression(
6712    parameter: Node<'_>,
6713    source: &str,
6714    parameter_names: &[String],
6715) -> Option<CppTemplateExpression> {
6716    let default = cpp_template_parameter_default(parameter)?;
6717    let base = cpp_template_expression(default, source, parameter_names);
6718    let Some(pointer_error) = parameter.next_named_sibling() else {
6719        return Some(base);
6720    };
6721    let Some(pointer_declarator) =
6722        recovered_abstract_pointer_declarator_term(pointer_error, source)
6723    else {
6724        return Some(base);
6725    };
6726    Some(CppTemplateExpression {
6727        text: format!(
6728            "{}{}",
6729            base.text,
6730            normalize_cpp_whitespace(node_text(pointer_error, source))
6731        ),
6732        term: CppTemplateTerm::Node {
6733            kind: "type_descriptor".to_string(),
6734            children: vec![base.term, pointer_declarator],
6735        },
6736    })
6737}
6738
6739fn recovered_abstract_pointer_declarator_term(
6740    node: Node<'_>,
6741    source: &str,
6742) -> Option<CppTemplateTerm> {
6743    if node.kind() != "ERROR" || node.child_count() == 0 {
6744        return None;
6745    }
6746    let mut children = Vec::new();
6747    for index in 0..node.child_count() {
6748        let child = node.child(index)?;
6749        if child.kind() != "*" {
6750            return None;
6751        }
6752        children.push(CppTemplateTerm::Atom {
6753            kind: "*".to_string(),
6754            text: normalize_cpp_whitespace(node_text(child, source)),
6755        });
6756    }
6757    Some(CppTemplateTerm::Node {
6758        kind: "abstract_pointer_declarator".to_string(),
6759        children,
6760    })
6761}
6762
6763fn cpp_template_expression(
6764    node: Node<'_>,
6765    source: &str,
6766    parameter_names: &[String],
6767) -> CppTemplateExpression {
6768    let text = normalize_cpp_whitespace(node_text(node, source));
6769    CppTemplateExpression {
6770        text,
6771        term: cpp_template_term(node, source, parameter_names),
6772    }
6773}
6774
6775pub fn cpp_template_term(
6776    node: Node<'_>,
6777    source: &str,
6778    parameter_names: &[String],
6779) -> CppTemplateTerm {
6780    enum Work<'tree> {
6781        Visit(Node<'tree>),
6782        Build { kind: String, child_count: usize },
6783    }
6784
6785    let mut work = vec![Work::Visit(node)];
6786    let mut terms = Vec::new();
6787    while let Some(next) = work.pop() {
6788        match next {
6789            Work::Visit(current) => {
6790                let text = normalize_cpp_whitespace(node_text(current, source));
6791                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names) {
6792                    terms.push(CppTemplateTerm::Parameter(text));
6793                    continue;
6794                }
6795                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
6796                    let mut cursor = current.walk();
6797                    let named = current
6798                        .named_children(&mut cursor)
6799                        .filter(|child| !child.is_extra() && child.kind() != "comment")
6800                        .collect::<Vec<_>>();
6801                    if let [child] = named.as_slice() {
6802                        work.push(Work::Visit(*child));
6803                        continue;
6804                    }
6805                }
6806                if current.child_count() == 0 {
6807                    terms.push(CppTemplateTerm::Atom {
6808                        kind: if matches!(
6809                            current.kind(),
6810                            "identifier"
6811                                | "type_identifier"
6812                                | "field_identifier"
6813                                | "namespace_identifier"
6814                        ) {
6815                            "identifier".to_string()
6816                        } else {
6817                            current.kind().to_string()
6818                        },
6819                        text,
6820                    });
6821                    continue;
6822                }
6823                let children = (0..current.child_count())
6824                    .filter_map(|index| current.child(index))
6825                    .filter(|child| !child.is_extra() && child.kind() != "comment")
6826                    .collect::<Vec<_>>();
6827                work.push(Work::Build {
6828                    kind: current.kind().to_string(),
6829                    child_count: children.len(),
6830                });
6831                work.extend(children.into_iter().rev().map(Work::Visit));
6832            }
6833            Work::Build { kind, child_count } => {
6834                let children = terms.split_off(terms.len() - child_count);
6835                terms.push(CppTemplateTerm::Node { kind, children });
6836            }
6837        }
6838    }
6839    terms.pop().expect("template term traversal emits one root")
6840}
6841
6842fn cpp_template_term_leaf_is_parameter(
6843    node: Node<'_>,
6844    text: &str,
6845    parameter_names: &[String],
6846) -> bool {
6847    if !parameter_names.iter().any(|parameter| parameter == text) {
6848        return false;
6849    }
6850    !node.parent().is_some_and(|parent| {
6851        matches!(
6852            parent.kind(),
6853            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
6854        ) && parent.child_by_field_name("scope").is_some()
6855            && parent.child_by_field_name("name") == Some(node)
6856    })
6857}
6858
6859fn enclosing_cpp_declaration_node(mut node: Node<'_>) -> Option<Node<'_>> {
6860    loop {
6861        match node.kind() {
6862            "declaration"
6863            | "function_declaration"
6864            | "field_declaration"
6865            | "function_definition" => return Some(node),
6866            _ => node = node.parent()?,
6867        }
6868    }
6869}
6870
6871fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
6872    let mut params = Vec::new();
6873    let mut cursor = parameters_node.walk();
6874    for child in parameters_node.children(&mut cursor) {
6875        match child.kind() {
6876            "parameter_declaration" | "optional_parameter_declaration" => {
6877                params.push(cpp_parameter_type(child, source));
6878            }
6879            "variadic_parameter_declaration" => {
6880                params.push(cpp_parameter_type(child, source));
6881            }
6882            "variadic_parameter" | "..." => params.push("...".to_string()),
6883            _ => {}
6884        }
6885    }
6886
6887    if params.is_empty() {
6888        "()".to_string()
6889    } else {
6890        format!("({})", params.join(", "))
6891    }
6892}
6893
6894fn cpp_signature_metadata(
6895    signature: String,
6896    function_declarator: Node<'_>,
6897    source: &str,
6898) -> SignatureMetadata {
6899    let dispatch = cpp_callable_dispatch_extensibility(function_declarator);
6900    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
6901    let return_type_text = cpp_callable_return_type_text(function_declarator, source);
6902    let return_type_identity = cpp_callable_return_type_identity(function_declarator, source);
6903    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
6904        return enrich(
6905            SignatureMetadata::new(signature, Vec::new())
6906                .with_return_type_text(return_type_text)
6907                .with_return_type_identity(return_type_identity),
6908        );
6909    };
6910    let callable_arity = cpp_callable_arity(parameters_node, source);
6911    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
6912    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
6913    let search_from = cpp_signature_search_start(&signature, function_declarator, source);
6914    let Some(relative_start) = signature
6915        .get(search_from..)
6916        .and_then(|suffix| suffix.find(&parameter_text))
6917    else {
6918        return enrich(
6919            SignatureMetadata::new(signature, Vec::new())
6920                .with_callable_arity(callable_arity)
6921                .with_callable_parameter_types(callable_parameter_types)
6922                .with_return_type_text(return_type_text)
6923                .with_return_type_identity(return_type_identity),
6924        );
6925    };
6926    let parameters_start = search_from + relative_start;
6927    let parameters_end = parameters_start + parameter_text.len();
6928    let mut search_start = parameters_start;
6929    let parameters = cpp_parameter_label_nodes(parameters_node)
6930        .into_iter()
6931        .filter_map(|label_node| {
6932            let label = normalize_cpp_whitespace(node_text(label_node, source));
6933            if label.is_empty() || search_start > parameters_end {
6934                return None;
6935            }
6936            let haystack = signature.get(search_start..parameters_end)?;
6937            let relative_start = haystack.find(&label)?;
6938            let start_byte = search_start + relative_start;
6939            let end_byte = start_byte + label.len();
6940            search_start = end_byte;
6941            Some(ParameterMetadata::new(label, start_byte, end_byte))
6942        })
6943        .collect();
6944    enrich(
6945        SignatureMetadata::new(signature, parameters)
6946            .with_callable_arity(callable_arity)
6947            .with_callable_parameter_types(callable_parameter_types)
6948            .with_return_type_text(return_type_text)
6949            .with_return_type_identity(return_type_identity),
6950    )
6951}
6952
6953fn cpp_callable_is_structural_constructor(function_declarator: Node<'_>, source: &str) -> bool {
6954    let Some(name_node) = function_declarator
6955        .child_by_field_name("declarator")
6956        .or_else(|| function_declarator.child_by_field_name("name"))
6957        .or_else(|| last_named_child(function_declarator))
6958    else {
6959        return false;
6960    };
6961    let Some(callable_name) = direct_identifier_name(name_node, source) else {
6962        return false;
6963    };
6964
6965    let mut current = function_declarator.parent();
6966    while let Some(ancestor) = current {
6967        let owner_name = match ancestor.kind() {
6968            "class_specifier" | "struct_specifier" | "union_specifier" => {
6969                class_like_name(ancestor, source)
6970            }
6971            "ERROR" => malformed_class_error_owner_name(ancestor, source),
6972            _ => None,
6973        };
6974        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
6975            return true;
6976        }
6977        current = ancestor.parent();
6978    }
6979    false
6980}
6981
6982/// Recover the owner name from the direct grammar shape retained when a later
6983/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
6984/// `ERROR` node:
6985///
6986/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
6987///
6988/// Direct-child checks keep this distinct from an unrelated nested class inside
6989/// a broader error region. The closing brace may be displaced past the error
6990/// node, so the opening body token is the available structural boundary.
6991fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
6992    if node.kind() != "ERROR" {
6993        return None;
6994    }
6995    let keyword = node.child(0)?;
6996    if !matches!(keyword.kind(), "class" | "struct" | "union") {
6997        return None;
6998    }
6999    let name_node = node.child(1)?;
7000    let name = direct_identifier_name(name_node, source)?;
7001    let has_body = (2..node.child_count())
7002        .filter_map(|index| node.child(index))
7003        .any(|child| child.kind() == "{");
7004    has_body.then_some(name)
7005}
7006
7007fn cpp_callable_return_type_identity(
7008    function_declarator: Node<'_>,
7009    source: &str,
7010) -> Option<StructuredTypeIdentity> {
7011    if cpp_callable_is_structural_constructor(function_declarator, source) {
7012        return None;
7013    }
7014    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7015    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7016    {
7017        return cpp_structured_type_identity(return_type, source, &lexical_scope);
7018    }
7019    let mut cursor = function_declarator.walk();
7020    if let Some(trailing) = function_declarator
7021        .named_children(&mut cursor)
7022        .find(|child| child.kind() == "trailing_return_type")
7023        && let Some(type_descriptor) = trailing.named_child(0)
7024    {
7025        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
7026    }
7027
7028    let mut current = function_declarator;
7029    let mut wrappers = Vec::new();
7030    while let Some(parent) = current.parent() {
7031        if matches!(
7032            parent.kind(),
7033            "function_definition" | "declaration" | "field_declaration"
7034        ) {
7035            let type_node = parent.child_by_field_name("type")?;
7036            if cpp_export_macro_token(node_text(type_node, source))
7037                && (0..parent.named_child_count()).any(|index| {
7038                    parent
7039                        .named_child(index)
7040                        .is_some_and(|child| child.kind() == "ERROR")
7041                })
7042            {
7043                return None;
7044            }
7045            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
7046            for wrapper in wrappers.into_iter().rev() {
7047                identity = cpp_wrap_structured_type(identity, wrapper)?;
7048            }
7049            return Some(identity);
7050        }
7051        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7052            || (matches!(
7053                parent.kind(),
7054                "pointer_declarator"
7055                    | "reference_declarator"
7056                    | "array_declarator"
7057                    | "parenthesized_declarator"
7058            ) && parent.named_child_count() == 1
7059                && parent.named_child(0) == Some(current));
7060        if !wraps_current_declarator {
7061            return None;
7062        }
7063        match parent.kind() {
7064            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
7065            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
7066            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
7067            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
7068            _ => return None,
7069        }
7070        current = parent;
7071    }
7072    None
7073}
7074
7075fn cpp_structured_type_identity(
7076    node: Node<'_>,
7077    source: &str,
7078    lexical_scope: &[String],
7079) -> Option<StructuredTypeIdentity> {
7080    enum Work<'tree> {
7081        Visit(Node<'tree>),
7082        Wrap(CppStructuredTypeWrapper),
7083        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
7084        BuildGeneric { argument_count: usize },
7085    }
7086
7087    let mut work = vec![Work::Visit(node)];
7088    let mut values = Vec::new();
7089    let mut builder = StructuredTypeIdentityBuilder::default();
7090    while let Some(next) = work.pop() {
7091        match next {
7092            Work::Visit(current) => match current.kind() {
7093                "type_descriptor" => {
7094                    let type_node = current
7095                        .child_by_field_name("type")
7096                        .or_else(|| current.named_child(0))?;
7097                    let mut wrappers = Vec::new();
7098                    let mut cursor = current.walk();
7099                    for child in current.named_children(&mut cursor) {
7100                        if child.id() != type_node.id() {
7101                            wrappers.extend(cpp_structured_declarator_wrappers(child));
7102                        }
7103                    }
7104                    work.push(Work::ApplyWrappers(wrappers));
7105                    work.push(Work::Visit(type_node));
7106                }
7107                "pointer_declarator" | "abstract_pointer_declarator" => {
7108                    let child = current
7109                        .child_by_field_name("declarator")
7110                        .or_else(|| current.named_child(0))?;
7111                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
7112                    work.push(Work::Visit(child));
7113                }
7114                "reference_declarator" => {
7115                    let child = current
7116                        .child_by_field_name("declarator")
7117                        .or_else(|| current.named_child(0))?;
7118                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
7119                    work.push(Work::Visit(child));
7120                }
7121                "array_declarator" | "abstract_array_declarator" => {
7122                    let child = current
7123                        .child_by_field_name("declarator")
7124                        .or_else(|| current.named_child(0))?;
7125                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
7126                    work.push(Work::Visit(child));
7127                }
7128                "template_type" => {
7129                    let name_node = current.child_by_field_name("name")?;
7130                    let arguments = current
7131                        .child_by_field_name("arguments")
7132                        .map(|arguments_node| {
7133                            let mut cursor = arguments_node.walk();
7134                            arguments_node
7135                                .named_children(&mut cursor)
7136                                .filter(|child| !child.is_extra() && child.kind() != "comment")
7137                                .collect::<Vec<_>>()
7138                        })
7139                        .unwrap_or_default();
7140                    work.push(Work::BuildGeneric {
7141                        argument_count: arguments.len(),
7142                    });
7143                    work.extend(arguments.into_iter().rev().map(Work::Visit));
7144                    work.push(Work::Visit(name_node));
7145                }
7146                "qualified_identifier"
7147                | "scoped_identifier"
7148                | "scoped_type_identifier"
7149                | "type_identifier"
7150                | "field_identifier"
7151                | "identifier"
7152                | "namespace_identifier"
7153                | "primitive_type" => {
7154                    values.push(builder.named(cpp_structured_named_type(
7155                        current,
7156                        source,
7157                        lexical_scope,
7158                    )?)?);
7159                }
7160                _ => {
7161                    let child = current.child_by_field_name("type").or_else(|| {
7162                        (current.named_child_count() == 1)
7163                            .then(|| current.named_child(0))
7164                            .flatten()
7165                    })?;
7166                    work.push(Work::Visit(child));
7167                }
7168            },
7169            Work::Wrap(wrapper) => {
7170                let root = values.pop()?;
7171                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7172            }
7173            Work::ApplyWrappers(wrappers) => {
7174                let mut root = values.pop()?;
7175                for wrapper in wrappers.into_iter().rev() {
7176                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7177                }
7178                values.push(root);
7179            }
7180            Work::BuildGeneric { argument_count } => {
7181                let value_count = argument_count.checked_add(1)?;
7182                let start = values.len().checked_sub(value_count)?;
7183                let mut built = values.split_off(start);
7184                let base = built.remove(0);
7185                values.push(builder.generic(base, built)?);
7186            }
7187        }
7188    }
7189    (values.len() == 1)
7190        .then(|| values.pop())
7191        .flatten()
7192        .and_then(|root| builder.finish(root))
7193}
7194
7195fn cpp_structured_named_type(
7196    node: Node<'_>,
7197    source: &str,
7198    lexical_scope: &[String],
7199) -> Option<StructuredTypeName> {
7200    let path = cpp_structured_type_path(node, source)?;
7201    let absolute = node.child_by_field_name("scope").is_none()
7202        && node.child(0).is_some_and(|child| child.kind() == "::");
7203    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7204}
7205
7206#[derive(Clone, Copy)]
7207enum CppStructuredTypeWrapper {
7208    Pointer,
7209    Reference,
7210    Array,
7211}
7212
7213fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7214    let mut wrappers = Vec::new();
7215    let mut current = node;
7216    loop {
7217        match current.kind() {
7218            "pointer_declarator" | "abstract_pointer_declarator" => {
7219                wrappers.push(CppStructuredTypeWrapper::Pointer)
7220            }
7221            "reference_declarator" | "abstract_reference_declarator" => {
7222                wrappers.push(CppStructuredTypeWrapper::Reference)
7223            }
7224            "array_declarator" | "abstract_array_declarator" => {
7225                wrappers.push(CppStructuredTypeWrapper::Array)
7226            }
7227            _ => break,
7228        }
7229        let Some(child) = current
7230            .child_by_field_name("declarator")
7231            .or_else(|| current.named_child(0))
7232        else {
7233            break;
7234        };
7235        current = child;
7236    }
7237    wrappers
7238}
7239
7240fn cpp_wrap_structured_type(
7241    identity: StructuredTypeIdentity,
7242    wrapper: CppStructuredTypeWrapper,
7243) -> Option<StructuredTypeIdentity> {
7244    match wrapper {
7245        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7246        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7247        CppStructuredTypeWrapper::Array => identity.wrap_array(),
7248    }
7249}
7250
7251fn cpp_wrap_structured_type_node(
7252    builder: &mut StructuredTypeIdentityBuilder,
7253    inner: StructuredTypeNodeId,
7254    wrapper: CppStructuredTypeWrapper,
7255) -> Option<StructuredTypeNodeId> {
7256    match wrapper {
7257        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7258        CppStructuredTypeWrapper::Reference => builder.reference(inner),
7259        CppStructuredTypeWrapper::Array => builder.array(inner),
7260    }
7261}
7262
7263fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7264    let mut path = Vec::new();
7265    let mut stack = vec![node];
7266    while let Some(current) = stack.pop() {
7267        match current.kind() {
7268            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7269                let component = node_text(current, source).to_string();
7270                if component.is_empty() {
7271                    return None;
7272                }
7273                path.push(component);
7274            }
7275            "template_type" | "dependent_type" => {
7276                stack.push(current.child_by_field_name("name")?);
7277            }
7278            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7279                stack.push(current.child_by_field_name("name")?);
7280                if let Some(scope) = current.child_by_field_name("scope") {
7281                    stack.push(scope);
7282                }
7283            }
7284            _ => return None,
7285        }
7286    }
7287    (!path.is_empty()).then_some(path)
7288}
7289
7290fn cpp_callable_lexical_scope(node: Node<'_>, source: &str) -> Vec<String> {
7291    let mut groups = Vec::new();
7292    let mut current = node.parent();
7293    while let Some(parent) = current {
7294        if matches!(
7295            parent.kind(),
7296            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7297        ) && let Some(name_node) = parent.child_by_field_name("name")
7298            && let Some(components) = cpp_structured_type_path(name_node, source)
7299            && !components.is_empty()
7300        {
7301            groups.push(components);
7302        }
7303        current = parent.parent();
7304    }
7305    groups.reverse();
7306    groups.into_iter().flatten().collect()
7307}
7308
7309fn cpp_callable_dispatch_extensibility(function_declarator: Node<'_>) -> DispatchExtensibility {
7310    let mut declaration = None;
7311    let mut current = Some(function_declarator);
7312    while let Some(node) = current {
7313        match node.kind() {
7314            "template_declaration"
7315            | "preproc_if"
7316            | "preproc_ifdef"
7317            | "preproc_else"
7318            | "preproc_elif"
7319            | "preproc_call"
7320            | "ERROR" => return DispatchExtensibility::Open,
7321            "declaration" | "field_declaration" | "function_definition" => {
7322                declaration.get_or_insert(node);
7323            }
7324            "translation_unit" => break,
7325            _ => {}
7326        }
7327        current = node.parent();
7328    }
7329    let Some(declaration) = declaration else {
7330        return DispatchExtensibility::Open;
7331    };
7332
7333    let mut saw_virtual_boundary = false;
7334    let mut stack = vec![declaration];
7335    while let Some(node) = stack.pop() {
7336        match node.kind() {
7337            "compound_statement" | "field_declaration_list" => continue,
7338            "final" | "final_specifier" => return DispatchExtensibility::Closed,
7339            "virtual"
7340            | "override"
7341            | "virtual_specifier"
7342            | "pure_virtual_clause"
7343            | "template_parameter_list"
7344            | "template_method"
7345            | "template_function"
7346            | "ERROR" => saw_virtual_boundary = true,
7347            _ => {}
7348        }
7349        let mut cursor = node.walk();
7350        stack.extend(node.children(&mut cursor));
7351    }
7352
7353    if saw_virtual_boundary {
7354        DispatchExtensibility::Open
7355    } else {
7356        DispatchExtensibility::Closed
7357    }
7358}
7359
7360fn cpp_callable_linkage(declaration: Node<'_>, source: &str) -> CallableLinkage {
7361    let mut enclosed_by_class = false;
7362    let mut current = declaration.parent();
7363    while let Some(node) = current {
7364        if node.kind() == "namespace_definition"
7365            && node
7366                .child_by_field_name("name")
7367                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7368        {
7369            return CallableLinkage::Internal;
7370        }
7371        if matches!(
7372            node.kind(),
7373            "class_specifier" | "struct_specifier" | "union_specifier"
7374        ) {
7375            if node
7376                .child_by_field_name("name")
7377                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7378            {
7379                return CallableLinkage::Internal;
7380            }
7381            enclosed_by_class = true;
7382        }
7383        if matches!(node.kind(), "function_definition" | "lambda_expression") {
7384            return CallableLinkage::Internal;
7385        }
7386        current = node.parent();
7387    }
7388
7389    if enclosed_by_class {
7390        return CallableLinkage::External;
7391    }
7392
7393    let mut cursor = declaration.walk();
7394    if declaration.named_children(&mut cursor).any(|child| {
7395        child.kind() == "storage_class_specifier"
7396            && normalize_cpp_whitespace(node_text(child, source)) == "static"
7397    }) {
7398        CallableLinkage::Internal
7399    } else {
7400        CallableLinkage::External
7401    }
7402}
7403
7404fn cpp_callable_return_type_text(function_declarator: Node<'_>, source: &str) -> Option<String> {
7405    if cpp_callable_is_structural_constructor(function_declarator, source) {
7406        return None;
7407    }
7408    if let Some((return_type, _)) = cpp_macro_displaced_callable_parts(function_declarator, source)
7409    {
7410        let text = normalize_cpp_whitespace(node_text(return_type, source));
7411        return (!text.is_empty()).then_some(text);
7412    }
7413    let mut cursor = function_declarator.walk();
7414    if let Some(trailing) = function_declarator
7415        .named_children(&mut cursor)
7416        .find(|child| child.kind() == "trailing_return_type")
7417        && let Some(type_descriptor) = trailing.named_child(0)
7418    {
7419        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7420        if !text.is_empty() {
7421            return Some(text);
7422        }
7423    }
7424
7425    let mut current = function_declarator;
7426    let mut indirection = String::new();
7427    while let Some(parent) = current.parent() {
7428        if matches!(
7429            parent.kind(),
7430            "function_definition" | "declaration" | "field_declaration"
7431        ) {
7432            let type_node = parent.child_by_field_name("type")?;
7433            if cpp_export_macro_token(node_text(type_node, source))
7434                && (0..parent.named_child_count()).any(|index| {
7435                    parent
7436                        .named_child(index)
7437                        .is_some_and(|child| child.kind() == "ERROR")
7438                })
7439            {
7440                // Export/decorator macros commonly occupy the grammar's `type`
7441                // field and leave the semantic return type in an ERROR sibling.
7442                // Do not persist the macro token as a return type. The malformed
7443                // declaration does not carry enough structured evidence here.
7444                return None;
7445            }
7446            let base = normalize_cpp_whitespace(node_text(type_node, source));
7447            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7448        }
7449        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7450            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7451                && parent.named_child_count() == 1
7452                && parent.named_child(0) == Some(current));
7453        if wraps_current_declarator {
7454            match parent.kind() {
7455                "pointer_declarator" => indirection.push('*'),
7456                "reference_declarator" => {
7457                    let reference = parent
7458                        .children(&mut parent.walk())
7459                        .find(|child| !child.is_named())
7460                        .map(|child| node_text(child, source))
7461                        .unwrap_or("&");
7462                    indirection.push_str(reference);
7463                }
7464                "init_declarator" | "parenthesized_declarator" => {}
7465                _ => return None,
7466            }
7467            current = parent;
7468            continue;
7469        }
7470        return None;
7471    }
7472    None
7473}
7474
7475fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7476    let mut required = 0;
7477    let mut total = 0;
7478    let mut repeated = false;
7479    let mut cursor = parameters_node.walk();
7480    for child in parameters_node.children(&mut cursor) {
7481        match child.kind() {
7482            "parameter_declaration" => {
7483                if cpp_parameter_is_explicit_object(child, source) {
7484                    continue;
7485                }
7486                if child.child_by_field_name("declarator").is_none()
7487                    && child
7488                        .child_by_field_name("type")
7489                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7490                {
7491                    continue;
7492                }
7493                required += 1;
7494                total += 1;
7495            }
7496            "optional_parameter_declaration" => total += 1,
7497            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7498                repeated = true;
7499            }
7500            _ => {}
7501        }
7502    }
7503    CallableArity::new(required, total, repeated)
7504}
7505
7506fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7507    parameter
7508        .child_by_field_name("type")
7509        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7510        .and_then(|type_node| type_node.child_by_field_name("constraint"))
7511        .is_some_and(|constraint| {
7512            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7513        })
7514}
7515
7516/// One entry of a callable's invocation parameter list.
7517///
7518/// The list excludes an explicit object parameter and a lone `void`, so its
7519/// length is the callable's invocation arity. Every derivation of a parameter
7520/// type - the rendered spelling used for overload discrimination and the
7521/// structured identity used by dependency-pack production - starts from this
7522/// same sequence, so the two can never disagree about which parameters exist.
7523#[derive(Clone, Copy)]
7524enum CppParameterSlot<'tree> {
7525    Declared(Node<'tree>),
7526    Ellipsis,
7527}
7528
7529fn cpp_callable_parameter_slots<'tree>(
7530    parameters_node: Node<'tree>,
7531    source: &str,
7532) -> Vec<CppParameterSlot<'tree>> {
7533    let mut slots = Vec::new();
7534    let mut cursor = parameters_node.walk();
7535    for parameter in parameters_node.children(&mut cursor) {
7536        match parameter.kind() {
7537            "parameter_declaration" | "optional_parameter_declaration" => {
7538                if cpp_parameter_is_explicit_object(parameter, source)
7539                    || (parameter.child_by_field_name("declarator").is_none()
7540                        && parameter
7541                            .child_by_field_name("type")
7542                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7543                {
7544                    continue;
7545                }
7546                slots.push(CppParameterSlot::Declared(parameter));
7547            }
7548            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7549                slots.push(CppParameterSlot::Ellipsis);
7550            }
7551            _ => {}
7552        }
7553    }
7554    slots
7555}
7556
7557fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7558    cpp_callable_parameter_slots(parameters_node, source)
7559        .into_iter()
7560        .map(|slot| match slot {
7561            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7562            CppParameterSlot::Ellipsis => "...".to_string(),
7563        })
7564        .collect()
7565}
7566
7567/// One callable parameter's parser-derived type.
7568///
7569/// A rendered spelling such as `const T&` is a source text, not a type name. A
7570/// consumer that must publish a type into a structured model - a semantic-pack
7571/// type reference, for example - reads this instead.
7572#[derive(Debug, Clone, PartialEq, Eq)]
7573pub enum CppParameterType {
7574    /// The written type reduced to a structured identity. C++ cv-qualifiers
7575    /// have no place in that model and are not represented.
7576    Structured(StructuredTypeIdentity),
7577    /// A `...` pack, which declares no parameter type at all.
7578    Ellipsis,
7579    /// A written type with no structured reduction, such as a macro-obscured,
7580    /// `decltype`-computed, or function-pointer parameter.
7581    Unstructured,
7582}
7583
7584/// The structured type of each invocation parameter, in declaration order.
7585///
7586/// The result is index-parallel with the rendered
7587/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
7588/// callable.
7589pub fn cpp_callable_parameter_type_identities(
7590    function_declarator: Node<'_>,
7591    source: &str,
7592) -> Vec<CppParameterType> {
7593    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7594        return Vec::new();
7595    };
7596    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7597    cpp_callable_parameter_slots(parameters_node, source)
7598        .into_iter()
7599        .map(|slot| match slot {
7600            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7601            CppParameterSlot::Declared(parameter) => {
7602                cpp_parameter_type_identity(parameter, source, &lexical_scope)
7603                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7604            }
7605        })
7606        .collect()
7607}
7608
7609fn cpp_parameter_type_identity(
7610    parameter: Node<'_>,
7611    source: &str,
7612    lexical_scope: &[String],
7613) -> Option<StructuredTypeIdentity> {
7614    let type_node = parameter.child_by_field_name("type")?;
7615    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7616    if let Some(declarator) = cpp_parameter_declarator(parameter) {
7617        for wrapper in cpp_structured_declarator_wrappers(declarator)
7618            .into_iter()
7619            .rev()
7620        {
7621            identity = cpp_wrap_structured_type(identity, wrapper)?;
7622        }
7623    }
7624    Some(identity)
7625}
7626
7627/// One callable parameter's comparable shape.
7628///
7629/// [`CppParameterType`] above answers "which type is written here" for a
7630/// structured model and deliberately records no cv-qualifiers, so it reports
7631/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
7632/// callable declarations declare one function needs the opposite trade: every
7633/// cv-qualifier that C++ counts as part of the parameter type must survive,
7634/// while the two declarations may spell the same type through different
7635/// qualifications. This slot carries that comparand.
7636///
7637/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
7638/// and with the rendered parameter spellings of the same callable.
7639#[derive(Debug, Clone, PartialEq, Eq)]
7640pub enum CppComparableSlot {
7641    /// A declared parameter reduced to its comparable shape.
7642    Shape(CppComparableParameter),
7643    /// A `...` pack, which declares no parameter type at all.
7644    Ellipsis,
7645    /// A parameter with no comparable reduction, such as a macro-obscured,
7646    /// `decltype`-computed, or function-pointer parameter.
7647    Unstructured,
7648}
7649
7650/// A parameter type as a flat arena of nodes plus a root index.
7651///
7652/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
7653/// can nest types very deeply, and cloning, comparing or dropping the value
7654/// must not consume the Rust call stack. Nodes are appended in post-order, so
7655/// every child index is smaller than its parent's and the last appended node is
7656/// the root.
7657///
7658/// That post-order append is also what makes the derived `PartialEq` a correct
7659/// structural equality: the builder below is deterministic, so one type shape
7660/// has exactly one arena layout no matter which spelling produced it. Two
7661/// shapes are equal as values iff they are equal as type trees.
7662#[derive(Debug, Clone, PartialEq, Eq)]
7663pub struct CppComparableParameter {
7664    nodes: Vec<CppComparableNode>,
7665    root: usize,
7666}
7667
7668/// One node of a [`CppComparableParameter`] arena.
7669///
7670/// `Reference` and `Array` carry no qualifiers because the grammar writes none
7671/// on them: a reference cannot be cv-qualified in C++, and an array's
7672/// qualifiers belong to its element type. A cv-qualifier written on a generic
7673/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
7674/// which is the only Named node the whole spelling produces.
7675#[derive(Debug, Clone, PartialEq, Eq)]
7676pub enum CppComparableNode {
7677    Named {
7678        name: StructuredTypeName,
7679        primitive: bool,
7680        konst: bool,
7681        volatil: bool,
7682    },
7683    Pointer {
7684        inner: usize,
7685        konst: bool,
7686        volatil: bool,
7687    },
7688    Reference {
7689        inner: usize,
7690    },
7691    Array {
7692        inner: usize,
7693    },
7694    Generic {
7695        base: usize,
7696        arguments: Vec<usize>,
7697    },
7698}
7699
7700impl CppComparableParameter {
7701    pub fn root(&self) -> usize {
7702        self.root
7703    }
7704
7705    pub fn node(&self, index: usize) -> &CppComparableNode {
7706        &self.nodes[index]
7707    }
7708
7709    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
7710    /// parameter's top level only.
7711    ///
7712    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
7713    /// declare one function, and a top-level array type becomes a pointer to
7714    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
7715    /// type constructor is this arena's root, which is why both adjustments
7716    /// are one match on it: cv on an inner pointer level, on a pointee, or on
7717    /// an array element keeps distinguishing the type, and an array behind a
7718    /// pointer or reference is not a top-level array.
7719    fn adjust_parameter_top_level(&mut self) {
7720        let root = self.root;
7721        match &mut self.nodes[root] {
7722            CppComparableNode::Named { konst, volatil, .. }
7723            | CppComparableNode::Pointer { konst, volatil, .. } => {
7724                *konst = false;
7725                *volatil = false;
7726            }
7727            CppComparableNode::Array { inner } => {
7728                let inner = *inner;
7729                self.nodes[root] = CppComparableNode::Pointer {
7730                    inner,
7731                    konst: false,
7732                    volatil: false,
7733                };
7734            }
7735            CppComparableNode::Generic { base, .. } => {
7736                let base = *base;
7737                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
7738                    unreachable!("a comparable generic's base is always a named leaf");
7739                };
7740                *konst = false;
7741                *volatil = false;
7742            }
7743            CppComparableNode::Reference { .. } => {}
7744        }
7745    }
7746}
7747
7748/// The comparable shape of each invocation parameter, in declaration order.
7749///
7750/// The result is index-parallel with
7751/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
7752/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
7753/// must treat as evidence of nothing rather than as agreement.
7754pub fn cpp_comparable_parameter_shapes(
7755    function_declarator: Node<'_>,
7756    source: &str,
7757) -> Vec<CppComparableSlot> {
7758    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7759        return Vec::new();
7760    };
7761    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source);
7762    cpp_callable_parameter_slots(parameters_node, source)
7763        .into_iter()
7764        .map(|slot| match slot {
7765            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
7766            CppParameterSlot::Declared(parameter) => {
7767                cpp_comparable_parameter(parameter, source, &lexical_scope)
7768                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
7769            }
7770        })
7771        .collect()
7772}
7773
7774fn cpp_comparable_parameter(
7775    parameter: Node<'_>,
7776    source: &str,
7777    lexical_scope: &[String],
7778) -> Option<CppComparableParameter> {
7779    let type_node = parameter.child_by_field_name("type")?;
7780    let levels = match cpp_parameter_declarator(parameter) {
7781        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
7782        None => Vec::new(),
7783    };
7784    let mut shape = cpp_comparable_type_shape(
7785        type_node,
7786        cpp_cv_qualifiers(parameter, source),
7787        levels,
7788        source,
7789        lexical_scope,
7790    )?;
7791    shape.adjust_parameter_top_level();
7792    Some(shape)
7793}
7794
7795/// The `const` and `volatile` qualifiers written as direct named children of
7796/// `node`.
7797///
7798/// The grammar exposes `type_qualifier` as a non-field named child in exactly
7799/// the three places a parameter's qualifiers can be written: on the
7800/// `parameter_declaration` itself (the base type), on a `type_descriptor`
7801/// (inside a template argument list), and on each `pointer_declarator` level
7802/// (the pointer object). Every other qualifier the grammar admits - `restrict`
7803/// and friends - takes no part in C++ type identity, the same filter
7804/// `cpp_parameter_type` applies to the rendered spelling (#1827).
7805fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
7806    let mut qualifiers = CppCvQualifiers::default();
7807    let mut cursor = node.walk();
7808    for child in node.named_children(&mut cursor) {
7809        if child.kind() != "type_qualifier" {
7810            continue;
7811        }
7812        match node_text(child, source) {
7813            "const" => qualifiers.konst = true,
7814            "volatile" => qualifiers.volatil = true,
7815            _ => {}
7816        }
7817    }
7818    qualifiers
7819}
7820
7821#[derive(Clone, Copy, Default)]
7822struct CppCvQualifiers {
7823    konst: bool,
7824    volatil: bool,
7825}
7826
7827impl CppCvQualifiers {
7828    fn union(self, other: Self) -> Self {
7829        Self {
7830            konst: self.konst || other.konst,
7831            volatil: self.volatil || other.volatil,
7832        }
7833    }
7834}
7835
7836/// One pointer, reference or array level a declarator chain adds.
7837#[derive(Clone, Copy)]
7838enum CppComparableLevel {
7839    Pointer { konst: bool, volatil: bool },
7840    Reference,
7841    Array,
7842}
7843
7844/// The levels `declarator` adds, outermost written level first.
7845///
7846/// C++ declarator syntax binds inside out: the level written closest to the
7847/// declared name is the outermost type constructor, and tree-sitter nests it
7848/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
7849/// applies in order to reach "array of pointer to int", and the qualifier of
7850/// `int * const *p` is read on the level it was written next to, the inner
7851/// pointer of the resulting type.
7852///
7853/// A declarator chain that names a function type - a function-pointer
7854/// parameter - has no comparable shape and reports `None`, matching the
7855/// structured identity channel.
7856fn cpp_comparable_declarator_levels(
7857    declarator: Node<'_>,
7858    source: &str,
7859) -> Option<Vec<CppComparableLevel>> {
7860    let mut levels = Vec::new();
7861    let mut current = declarator;
7862    loop {
7863        match current.kind() {
7864            "pointer_declarator" | "abstract_pointer_declarator" => {
7865                let qualifiers = cpp_cv_qualifiers(current, source);
7866                levels.push(CppComparableLevel::Pointer {
7867                    konst: qualifiers.konst,
7868                    volatil: qualifiers.volatil,
7869                });
7870            }
7871            "reference_declarator" | "abstract_reference_declarator" => {
7872                levels.push(CppComparableLevel::Reference);
7873            }
7874            "array_declarator" | "abstract_array_declarator" => {
7875                levels.push(CppComparableLevel::Array);
7876            }
7877            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
7878            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
7879            _ => return None,
7880        }
7881        let Some(next) = cpp_nested_declarator(current) else {
7882            return Some(levels);
7883        };
7884        current = next;
7885    }
7886}
7887
7888/// Reduce one written type to a comparable arena.
7889///
7890/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
7891/// two additions: each visited type node carries the cv-qualifiers written on
7892/// it, and declarator levels arrive as a prepared list rather than being
7893/// rediscovered inside the walk.
7894fn cpp_comparable_type_shape(
7895    type_node: Node<'_>,
7896    qualifiers: CppCvQualifiers,
7897    levels: Vec<CppComparableLevel>,
7898    source: &str,
7899    lexical_scope: &[String],
7900) -> Option<CppComparableParameter> {
7901    enum Work<'tree> {
7902        Visit {
7903            node: Node<'tree>,
7904            qualifiers: CppCvQualifiers,
7905        },
7906        ApplyLevels(Vec<CppComparableLevel>),
7907        BuildGeneric {
7908            argument_count: usize,
7909        },
7910    }
7911
7912    let mut nodes: Vec<CppComparableNode> = Vec::new();
7913    let mut values: Vec<usize> = Vec::new();
7914    let mut work = vec![
7915        Work::ApplyLevels(levels),
7916        Work::Visit {
7917            node: type_node,
7918            qualifiers,
7919        },
7920    ];
7921    while let Some(next) = work.pop() {
7922        match next {
7923            Work::Visit { node, qualifiers } => match node.kind() {
7924                "type_descriptor" => {
7925                    let inner_type = node
7926                        .child_by_field_name("type")
7927                        .or_else(|| node.named_child(0))?;
7928                    let mut cursor = node.walk();
7929                    let declarator = node.child_by_field_name("declarator").or_else(|| {
7930                        node.named_children(&mut cursor).find(|child| {
7931                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
7932                        })
7933                    });
7934                    let levels = match declarator {
7935                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
7936                        None => Vec::new(),
7937                    };
7938                    work.push(Work::ApplyLevels(levels));
7939                    work.push(Work::Visit {
7940                        node: inner_type,
7941                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
7942                    });
7943                }
7944                "sized_type_specifier" => {
7945                    // `unsigned char` is one primitive type whose components are
7946                    // partly unnamed tokens, so the whole specifier is its own
7947                    // name component. Reducing it to the `type` child would make
7948                    // `f(unsigned char)` and `f(char)` compare equal.
7949                    let name = StructuredTypeName::new(
7950                        vec![normalize_cpp_whitespace(node_text(node, source))],
7951                        lexical_scope.to_vec(),
7952                        false,
7953                    )?;
7954                    values.push(cpp_push_comparable_node(
7955                        &mut nodes,
7956                        CppComparableNode::Named {
7957                            name,
7958                            primitive: true,
7959                            konst: qualifiers.konst,
7960                            volatil: qualifiers.volatil,
7961                        },
7962                    ));
7963                }
7964                "qualified_identifier"
7965                | "scoped_identifier"
7966                | "scoped_type_identifier"
7967                | "type_identifier"
7968                | "field_identifier"
7969                | "identifier"
7970                | "namespace_identifier"
7971                | "primitive_type"
7972                | "template_type" => {
7973                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
7974                    values.push(cpp_push_comparable_node(
7975                        &mut nodes,
7976                        CppComparableNode::Named {
7977                            name,
7978                            primitive: node.kind() == "primitive_type",
7979                            konst: qualifiers.konst,
7980                            volatil: qualifiers.volatil,
7981                        },
7982                    ));
7983                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
7984                        let mut cursor = arguments_node.walk();
7985                        let arguments = arguments_node
7986                            .named_children(&mut cursor)
7987                            .filter(|child| !child.is_extra() && child.kind() != "comment")
7988                            .collect::<Vec<_>>();
7989                        work.push(Work::BuildGeneric {
7990                            argument_count: arguments.len(),
7991                        });
7992                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
7993                            node: argument,
7994                            qualifiers: CppCvQualifiers::default(),
7995                        }));
7996                    }
7997                }
7998                _ => {
7999                    let inner = node.child_by_field_name("type").or_else(|| {
8000                        (node.named_child_count() == 1)
8001                            .then(|| node.named_child(0))
8002                            .flatten()
8003                    })?;
8004                    work.push(Work::Visit {
8005                        node: inner,
8006                        qualifiers,
8007                    });
8008                }
8009            },
8010            Work::ApplyLevels(levels) => {
8011                let mut root = values.pop()?;
8012                for level in levels {
8013                    let node = match level {
8014                        CppComparableLevel::Pointer { konst, volatil } => {
8015                            CppComparableNode::Pointer {
8016                                inner: root,
8017                                konst,
8018                                volatil,
8019                            }
8020                        }
8021                        CppComparableLevel::Reference => {
8022                            CppComparableNode::Reference { inner: root }
8023                        }
8024                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
8025                    };
8026                    root = cpp_push_comparable_node(&mut nodes, node);
8027                }
8028                values.push(root);
8029            }
8030            Work::BuildGeneric { argument_count } => {
8031                let value_count = argument_count.checked_add(1)?;
8032                let start = values.len().checked_sub(value_count)?;
8033                let mut built = values.split_off(start);
8034                let base = built.remove(0);
8035                values.push(cpp_push_comparable_node(
8036                    &mut nodes,
8037                    CppComparableNode::Generic {
8038                        base,
8039                        arguments: built,
8040                    },
8041                ));
8042            }
8043        }
8044    }
8045    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
8046    debug_assert_eq!(
8047        root,
8048        nodes.len().saturating_sub(1),
8049        "comparable nodes are appended in post-order, so the root is the last one"
8050    );
8051    Some(CppComparableParameter { nodes, root })
8052}
8053
8054fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
8055    nodes.push(node);
8056    nodes.len() - 1
8057}
8058
8059/// The template argument list of the name `node` terminates in, if any.
8060///
8061/// `std::vector<int>` writes its arguments on the `name` of a qualified
8062/// identifier, so a walk that stopped at the qualified node would reduce
8063/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
8064fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
8065    let mut current = node;
8066    loop {
8067        match current.kind() {
8068            "template_type" => return current.child_by_field_name("arguments"),
8069            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8070                current = current.child_by_field_name("name")?;
8071            }
8072            _ => return None,
8073        }
8074    }
8075}
8076
8077/// The callable declarator of the declaration that covers `start_byte`.
8078///
8079/// A consumer that holds a declaration's recorded byte position rather than its
8080/// syntax node - external header extraction, for instance - uses this to reach
8081/// the same `function_declarator` the declaration walk read.
8082pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
8083    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
8084    loop {
8085        if matches!(
8086            current.kind(),
8087            "declaration" | "field_declaration" | "function_definition"
8088        ) && let Some(declarator) = current
8089            .child_by_field_name("declarator")
8090            .and_then(extract_function_declarator)
8091        {
8092            return Some(declarator);
8093        }
8094        current = current.parent()?;
8095    }
8096}
8097
8098fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
8099    let mut labels = Vec::new();
8100    let mut cursor = parameters_node.walk();
8101    for child in parameters_node.children(&mut cursor) {
8102        match child.kind() {
8103            "parameter_declaration" | "optional_parameter_declaration" => {
8104                if let Some(name_node) = child
8105                    .child_by_field_name("declarator")
8106                    .and_then(cpp_declarator_label_node)
8107                {
8108                    labels.push(name_node);
8109                } else {
8110                    labels.push(child);
8111                }
8112            }
8113            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8114                labels.push(child);
8115            }
8116            _ => {}
8117        }
8118    }
8119    labels
8120}
8121
8122fn cpp_signature_search_start(
8123    signature: &str,
8124    function_declarator: Node<'_>,
8125    source: &str,
8126) -> usize {
8127    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator) else {
8128        return 0;
8129    };
8130    let raw = node_text(enclosing, source);
8131    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
8132    let offset = function_declarator
8133        .start_byte()
8134        .saturating_sub(enclosing.start_byte())
8135        .saturating_sub(leading_trim_bytes);
8136    offset.min(signature.len())
8137}
8138
8139fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
8140    match node.kind() {
8141        "identifier" | "field_identifier" => Some(node),
8142        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
8143            .child_by_field_name("declarator")
8144            .or_else(|| last_named_child(node))
8145            .and_then(cpp_declarator_label_node),
8146        "array_declarator" => node
8147            .child_by_field_name("declarator")
8148            .and_then(cpp_declarator_label_node),
8149        "function_declarator" => node
8150            .child_by_field_name("declarator")
8151            .or_else(|| node.child_by_field_name("name"))
8152            .or_else(|| last_named_child(node))
8153            .and_then(cpp_declarator_label_node),
8154        _ => None,
8155    }
8156}
8157
8158fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
8159    let base_type = parameter
8160        .child_by_field_name("type")
8161        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
8162        .unwrap_or_default();
8163    let declarator = cpp_parameter_declarator(parameter);
8164    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
8165    // are discarded, so `f(const int)` and `f(int)` declare one function. A
8166    // qualifier written next to the parameter's type is only top-level when
8167    // the declarator adds no indirection; behind a pointer, reference or array
8168    // declarator the same qualifier belongs to the pointee, referent or
8169    // element and keeps distinguishing the type (#1827).
8170    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
8171    let mut cursor = parameter.walk();
8172    let qualifiers = parameter
8173        .named_children(&mut cursor)
8174        .filter(|child| child.kind() == "type_qualifier")
8175        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8176        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
8177        .collect::<Vec<_>>()
8178        .join(" ");
8179    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
8180        (true, _) => base_type,
8181        (_, true) => qualifiers,
8182        (false, false) => format!("{qualifiers} {base_type}"),
8183    };
8184    let declarator_suffix = declarator
8185        .map(|node| cpp_declarator_suffix_without_name(node, source))
8186        .unwrap_or_default();
8187
8188    let combined = if type_text.is_empty() {
8189        declarator_suffix
8190    } else if declarator_suffix.is_empty() {
8191        type_text
8192    } else {
8193        format!("{type_text} {declarator_suffix}")
8194    };
8195    normalize_cpp_type_text(&combined)
8196}
8197
8198fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
8199    parameter.child_by_field_name("declarator").or_else(|| {
8200        // Some unnamed prototype parameters expose their abstract declarator
8201        // as a direct named child without the grammar's `declarator` field.
8202        // Recover only the structured abstract-declarator node; the parameter's
8203        // type and qualifiers are distinct children and must not be guessed from
8204        // source text.
8205        let mut cursor = parameter.walk();
8206        parameter
8207            .named_children(&mut cursor)
8208            .find(|child| is_cpp_abstract_declarator(child.kind()))
8209    })
8210}
8211
8212/// Whether a parameter's declarator chain adds indirection - a pointer,
8213/// reference, array or function declarator - to the parameter's written type.
8214pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
8215    let mut current = Some(declarator);
8216    while let Some(node) = current {
8217        if matches!(
8218            node.kind(),
8219            "pointer_declarator"
8220                | "abstract_pointer_declarator"
8221                | "reference_declarator"
8222                | "abstract_reference_declarator"
8223                | "array_declarator"
8224                | "abstract_array_declarator"
8225                | "function_declarator"
8226                | "abstract_function_declarator"
8227        ) {
8228            return true;
8229        }
8230        current = cpp_nested_declarator(node);
8231    }
8232    false
8233}
8234
8235fn is_cpp_abstract_declarator(kind: &str) -> bool {
8236    matches!(
8237        kind,
8238        "abstract_pointer_declarator"
8239            | "abstract_reference_declarator"
8240            | "abstract_array_declarator"
8241            | "abstract_function_declarator"
8242            | "abstract_parenthesized_declarator"
8243    )
8244}
8245
8246fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
8247    node.child_by_field_name("declarator").or_else(|| {
8248        if is_cpp_abstract_declarator(node.kind()) {
8249            let mut cursor = node.walk();
8250            node.named_children(&mut cursor)
8251                .find(|child| is_cpp_abstract_declarator(child.kind()))
8252        } else {
8253            // Named declarators historically use their last named child when
8254            // tree-sitter omits the field. Keep that broad fallback for
8255            // attributed, variadic, and recovered named shapes.
8256            last_named_child(node)
8257        }
8258    })
8259}
8260
8261fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
8262    match node.kind() {
8263        "identifier" | "field_identifier" => String::new(),
8264        "pointer_declarator" | "abstract_pointer_declarator" => {
8265            let inner = cpp_nested_declarator(node)
8266                .map(|child| cpp_declarator_suffix_without_name(child, source))
8267                .unwrap_or_default();
8268            format!("*{inner}")
8269        }
8270        "reference_declarator" | "abstract_reference_declarator" => {
8271            let inner = cpp_nested_declarator(node)
8272                .map(|child| cpp_declarator_suffix_without_name(child, source))
8273                .unwrap_or_default();
8274            let reference = node
8275                .children(&mut node.walk())
8276                .find(|child| matches!(child.kind(), "&" | "&&"))
8277                .map(|child| node_text(child, source))
8278                .unwrap_or("&");
8279            format!("{reference}{inner}")
8280        }
8281        "array_declarator" | "abstract_array_declarator" => {
8282            let inner = cpp_nested_declarator(node)
8283                .map(|child| cpp_declarator_suffix_without_name(child, source))
8284                .unwrap_or_default();
8285            let size = node
8286                .child_by_field_name("size")
8287                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8288                .unwrap_or_default();
8289            format!("{inner}[{size}]")
8290        }
8291        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
8292            let inner = cpp_nested_declarator(node);
8293            inner
8294                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
8295                .unwrap_or_default()
8296        }
8297        "function_declarator" | "abstract_function_declarator" => {
8298            let inner = cpp_nested_declarator(node)
8299                .map(|child| cpp_declarator_suffix_without_name(child, source))
8300                .unwrap_or_default();
8301            let params = node
8302                .child_by_field_name("parameters")
8303                .map(|child| cpp_parameter_signature(child, source))
8304                .unwrap_or_else(|| "()".to_string());
8305            format!("{inner}{params}")
8306        }
8307        _ => {
8308            let text = normalize_cpp_whitespace(node_text(node, source));
8309            let name = extract_declarator_name(node, source);
8310            if name.is_empty() {
8311                text
8312            } else {
8313                text.replace(&name, "").trim().to_string()
8314            }
8315        }
8316    }
8317}
8318
8319fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
8320    collapse_cpp_whitespace(
8321        suffix
8322            .trim()
8323            .trim_start_matches("->")
8324            .trim_start_matches('{')
8325            .trim_end_matches(';'),
8326    )
8327}
8328
8329pub fn normalize_cpp_whitespace(value: &str) -> String {
8330    collapse_cpp_whitespace(value)
8331}
8332
8333fn normalize_cpp_type_text(value: &str) -> String {
8334    collapse_cpp_whitespace(value)
8335        .replace(", ", ",")
8336        .replace(" <", "<")
8337        .replace("< ", "<")
8338        .replace(" >", ">")
8339}
8340
8341fn collapse_cpp_whitespace(value: &str) -> String {
8342    let mut result = String::new();
8343    let mut prev_space = false;
8344    for ch in value.chars() {
8345        if ch.is_whitespace() {
8346            if !prev_space {
8347                result.push(' ');
8348            }
8349            prev_space = true;
8350        } else {
8351            result.push(ch);
8352            prev_space = false;
8353        }
8354    }
8355    result.trim().to_string()
8356}
8357
8358pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
8359    node_source_text(node, source)
8360}
8361
8362pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
8363    walk_named_tree_preorder(node, true, |node| {
8364        match node.kind() {
8365            "type_identifier" | "identifier" | "qualified_identifier" => {
8366                let text = node_text(node, source).trim();
8367                if !text.is_empty() {
8368                    identifiers.insert(text.to_string());
8369                }
8370            }
8371            _ => {}
8372        }
8373        WalkControl::Continue
8374    });
8375}
8376
8377fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
8378    node.child_by_field_name("body").or_else(|| {
8379        let mut cursor = node.walk();
8380        node.named_children(&mut cursor).find(|child| {
8381            matches!(
8382                child.kind(),
8383                "declaration_list" | "field_declaration_list" | "enumerator_list"
8384            )
8385        })
8386    })
8387}
8388
8389/// Return a class body's actual closing brace when the parser supplied one.
8390///
8391/// A malformed namespace sentinel can leave a class node carrying unrelated
8392/// parser errors even though its own class body is complete.  `has_error()` is
8393/// therefore too coarse an admission predicate for sentinel ownership.  The
8394/// body list, however, exposes the opening and closing punctuation directly;
8395/// a real (non-missing) final `}` proves that the class did not borrow the
8396/// enclosing namespace's close.  Requiring the body to end before its parent
8397/// container also rejects a recovered node whose body swallowed that outer
8398/// boundary.
8399fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
8400    if !matches!(
8401        node.kind(),
8402        "class_specifier" | "struct_specifier" | "union_specifier"
8403    ) {
8404        return None;
8405    }
8406    let body = cpp_body_node(node)?;
8407    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
8408        return None;
8409    }
8410    let open = body.child(0)?;
8411    let close = body.child(body.child_count().checked_sub(1)?)?;
8412    if open.kind() != "{"
8413        || open.is_missing()
8414        || close.kind() != "}"
8415        || close.is_missing()
8416        || close.end_byte() != body.end_byte()
8417        || body.end_byte() > node.end_byte()
8418        || node
8419            .parent()
8420            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
8421    {
8422        return None;
8423    }
8424    Some(close)
8425}
8426
8427fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
8428    if node.kind() == "namespace_definition" {
8429        return true;
8430    }
8431    let mut cursor = node.walk();
8432    node.named_children(&mut cursor)
8433        .any(cpp_contains_namespace_definition)
8434}
8435
8436struct CppNestedNamespaceSentinel<'tree> {
8437    function: Node<'tree>,
8438    body: Node<'tree>,
8439    namespace_components: Vec<String>,
8440}
8441
8442/// Owned structural recovery metadata for a namespace-sentinel region.
8443///
8444/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
8445/// instead of the namespace/class scopes that the declaration visitor restores.
8446/// The inverted usage walk has the original CST, so it needs the same ownership
8447/// evidence without borrowing parser nodes across its file scan.  Keep this
8448/// descriptor deliberately source-range based: callers can match a reference
8449/// node by containment and then resolve its structured type spelling in the
8450/// recovered class scope.
8451#[derive(Debug, Clone)]
8452pub struct CppSentinelRecoveredOwner {
8453    pub range: Range,
8454    /// Start of the qualified owner name (`btree<P>::method`).  A leading
8455    /// return type before this byte is looked up from the namespace; parameters,
8456    /// trailing returns, and the body use the member owner scope.
8457    pub owner_name_start_byte: usize,
8458    /// Number of leading components belonging to the namespace rather than
8459    /// the qualified class owner.  A leading return type is looked up before
8460    /// every owner component, not merely before the innermost class.
8461    pub namespace_component_count: usize,
8462    pub scope_components: Vec<String>,
8463}
8464
8465#[derive(Debug, Clone)]
8466pub struct CppSentinelRecoveredClass {
8467    pub namespace_range: Range,
8468    pub namespace_scope_components: Vec<String>,
8469    pub class_range: Range,
8470    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
8471    pub scope_components: Vec<String>,
8472    /// Qualified out-of-line member definitions owned by this class.  Their
8473    /// ranges may extend beyond `class_range` when the malformed sentinel
8474    /// swallowed the namespace close and left definitions as function siblings.
8475    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
8476}
8477
8478/// Resolve the lexical scope restored for a node in a malformed
8479/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
8480/// outrank class spans, which in turn outrank the surviving namespace body.
8481/// The class ancestor suffix is recovered from the original CST so nested
8482/// members keep their complete `Outer::Inner` owner chain.
8483pub fn cpp_sentinel_recovered_scope_for_node(
8484    node: Node<'_>,
8485    source: &str,
8486    recovered_classes: &[CppSentinelRecoveredClass],
8487) -> Option<Vec<String>> {
8488    let contains =
8489        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
8490    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
8491    for recovered in recovered_classes {
8492        for owner in recovered
8493            .owner_ranges
8494            .iter()
8495            .filter(|owner| contains(owner.range))
8496        {
8497            let replace = best_owner.is_none_or(|existing| {
8498                owner.range.end_byte.saturating_sub(owner.range.start_byte)
8499                    < existing
8500                        .range
8501                        .end_byte
8502                        .saturating_sub(existing.range.start_byte)
8503            });
8504            if replace {
8505                best_owner = Some(owner);
8506            }
8507        }
8508    }
8509    if let Some(owner) = best_owner {
8510        let mut scope = owner.scope_components.clone();
8511        if node.start_byte() < owner.owner_name_start_byte {
8512            scope.truncate(owner.namespace_component_count);
8513        }
8514        return Some(scope);
8515    }
8516
8517    let class = recovered_classes
8518        .iter()
8519        .filter(|recovered| contains(recovered.class_range))
8520        .min_by_key(|recovered| {
8521            recovered
8522                .class_range
8523                .end_byte
8524                .saturating_sub(recovered.class_range.start_byte)
8525        });
8526    let class_scope = class.is_some();
8527    let mut scope = if let Some(class) = class {
8528        class.scope_components.clone()
8529    } else {
8530        let namespace = recovered_classes
8531            .iter()
8532            .filter(|recovered| contains(recovered.namespace_range))
8533            .min_by_key(|recovered| {
8534                recovered
8535                    .namespace_range
8536                    .end_byte
8537                    .saturating_sub(recovered.namespace_range.start_byte)
8538            })?;
8539        let mut scope = namespace.namespace_scope_components.clone();
8540        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
8541        let common_prefix = scope
8542            .iter()
8543            .zip(&parser_namespace)
8544            .take_while(|(recovered, parser)| recovered == parser)
8545            .count();
8546        scope.extend(parser_namespace.into_iter().skip(common_prefix));
8547        scope
8548    };
8549    if class_scope {
8550        let mut ancestor_components = Vec::new();
8551        let mut ancestor = node.parent();
8552        while let Some(current) = ancestor {
8553            if matches!(
8554                current.kind(),
8555                "class_specifier" | "struct_specifier" | "union_specifier"
8556            ) && let Some(name) = current.child_by_field_name("name")
8557                && let Some(name_components) = cpp_name_components(name, source)
8558            {
8559                ancestor_components.push(
8560                    name_components
8561                        .into_iter()
8562                        .map(|component| component.name)
8563                        .collect::<Vec<_>>(),
8564                );
8565            }
8566            ancestor = current.parent();
8567        }
8568        ancestor_components.reverse();
8569        let base_len = scope.len();
8570        for component in ancestor_components.into_iter().flatten() {
8571            if scope.len() >= base_len && scope.last() == Some(&component) {
8572                continue;
8573            }
8574            scope.push(component);
8575        }
8576    }
8577    Some(scope)
8578}
8579
8580struct CppSentinelFragmentedClassTail<'tree> {
8581    class_node: Node<'tree>,
8582    template_node: Option<Node<'tree>>,
8583    name: String,
8584    raw_supertypes: Option<Vec<String>>,
8585    fragmented: FragmentedExportBody,
8586    consumed_start: usize,
8587}
8588
8589struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8590    name: String,
8591    open: Node<'tree>,
8592    raw_supertypes: Option<Vec<String>>,
8593}
8594
8595struct CppSentinelDirectBodyClassRegion {
8596    namespace_components: Vec<String>,
8597    class_start: usize,
8598    class_start_line: usize,
8599    class_close_end: usize,
8600    class_close_line: usize,
8601    name: String,
8602}
8603
8604fn cpp_sentinel_body_class_candidate<'tree>(
8605    child: Node<'tree>,
8606) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8607    if matches!(
8608        child.kind(),
8609        "class_specifier" | "struct_specifier" | "union_specifier"
8610    ) {
8611        return Some((child, None));
8612    }
8613    if child.kind() != "template_declaration" {
8614        if child.kind() == "declaration" {
8615            return Some((first_class_like_child(child)?, None));
8616        }
8617        return None;
8618    }
8619    let mut cursor = child.walk();
8620    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8621        if matches!(
8622            candidate.kind(),
8623            "class_specifier" | "struct_specifier" | "union_specifier"
8624        ) {
8625            Some(candidate)
8626        } else if candidate.kind() == "declaration" {
8627            first_class_like_child(candidate)
8628        } else {
8629            None
8630        }
8631    })?;
8632    Some((class_node, Some(child)))
8633}
8634
8635/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
8636/// namespace-sentinel body when a later member macro ends the bogus sentinel
8637/// function before the real class close. The anonymous class/open tokens and
8638/// direct identifier are the structural proof; a retained direct close would
8639/// be an ordinary malformed class rather than the fragmented tail handled here.
8640fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8641    node: Node<'tree>,
8642    source: &str,
8643) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8644    let name = malformed_class_error_owner_name(node, source)?;
8645    let mut cursor = node.walk();
8646    let children = node.children(&mut cursor).collect::<Vec<_>>();
8647    let keyword = children.first()?;
8648    let open_index = children.iter().position(|child| child.kind() == "{")?;
8649    if children[open_index + 1..]
8650        .iter()
8651        .any(|child| child.kind() == "}")
8652    {
8653        return None;
8654    }
8655    let raw_supertypes =
8656        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8657    Some(CppSentinelFragmentedClassErrorPrefix {
8658        name,
8659        open: children[open_index],
8660        raw_supertypes,
8661    })
8662}
8663
8664fn cpp_sentinel_direct_body_class_candidate<'tree>(
8665    child: Node<'tree>,
8666) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8667    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
8668        return Some(candidate);
8669    }
8670    if child.kind() != "template_declaration" {
8671        return None;
8672    }
8673    let mut cursor = child.walk();
8674    let wrapper = child
8675        .named_children(&mut cursor)
8676        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
8677    Some((first_class_like_child(wrapper)?, Some(child)))
8678}
8679
8680fn cpp_sentinel_direct_namespace_components(
8681    function: Node<'_>,
8682    body: Node<'_>,
8683    source: &str,
8684) -> Option<Vec<String>> {
8685    let mut cursor = function.walk();
8686    let children = function
8687        .named_children(&mut cursor)
8688        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
8689        .collect::<Vec<_>>();
8690    let sentinel_index = children.iter().rposition(|child| {
8691        direct_identifier_name(*child, source)
8692            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
8693    })?;
8694    let mut identifiers = Vec::new();
8695    let mut stack = children[sentinel_index + 1..]
8696        .iter()
8697        .rev()
8698        .copied()
8699        .collect::<Vec<_>>();
8700    while let Some(current) = stack.pop() {
8701        if let Some(name) = direct_identifier_name(current, source) {
8702            identifiers.push(name);
8703            continue;
8704        }
8705        let mut cursor = current.walk();
8706        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
8707        stack.extend(children.into_iter().rev());
8708    }
8709    let [keyword, namespace] = identifiers.as_slice() else {
8710        return None;
8711    };
8712    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
8713        .then(|| vec![namespace.clone()])
8714}
8715
8716fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
8717    let mut sibling = class_semicolon.next_named_sibling();
8718    let namespace_close = loop {
8719        let Some(current) = sibling else {
8720            return false;
8721        };
8722        sibling = current.next_named_sibling();
8723        if current.kind() != "comment" {
8724            break current;
8725        }
8726    };
8727    if !cpp_is_stray_close_brace(namespace_close, source) {
8728        return false;
8729    }
8730    loop {
8731        let Some(current) = sibling else {
8732            return false;
8733        };
8734        sibling = current.next_named_sibling();
8735        if current.kind() == "comment" {
8736            continue;
8737        }
8738        return direct_identifier_name(current, source)
8739            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
8740    }
8741}
8742
8743fn cpp_sentinel_macro_body_class_region(
8744    node: Node<'_>,
8745    source: &str,
8746) -> Option<CppSentinelDirectBodyClassRegion> {
8747    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
8748        return None;
8749    };
8750    if node.kind() != "function_definition" || !node.has_error() {
8751        return None;
8752    }
8753    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
8754    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
8755    let mut cursor = body.walk();
8756    let candidates = body
8757        .named_children(&mut cursor)
8758        .filter_map(cpp_sentinel_direct_body_class_candidate)
8759        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
8760        .collect::<Vec<_>>();
8761    let [(class_node, template_node)] = candidates.as_slice() else {
8762        return None;
8763    };
8764    let original_body = cpp_body_node(*class_node)?;
8765    let name = class_like_name(*class_node, source)?;
8766    if name.is_empty() || cpp_export_macro_token(&name) {
8767        return None;
8768    }
8769
8770    let mut sibling = node.next_named_sibling();
8771    let (class_close_start, class_close_end, class_close_line) = loop {
8772        let current = sibling?;
8773        let next = current.next_named_sibling();
8774        if cpp_is_stray_close_brace(current, source)
8775            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
8776        {
8777            let semicolon = next.expect("checked above");
8778            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
8779                return None;
8780            }
8781            break (
8782                current.start_byte(),
8783                semicolon.end_byte(),
8784                semicolon.end_position().row + 1,
8785            );
8786        }
8787        sibling = next;
8788    };
8789    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
8790    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
8791    let root = tree.root_node();
8792    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
8793    let reparsed = cpp_sentinel_reparsed_class(root, reparsed_template, source)?;
8794    if reparsed.name != name
8795        || reparsed.declaration_node.start_byte() != class_node.start_byte()
8796        || reparsed.body.start_byte() != original_body.start_byte()
8797        || class_close_start <= reparsed.body.end_byte()
8798        || class_close_end <= class_node.end_byte()
8799    {
8800        return None;
8801    }
8802    Some(CppSentinelDirectBodyClassRegion {
8803        namespace_components,
8804        class_start: reparse_start,
8805        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
8806            node.start_position().row + 1
8807        }),
8808        class_close_end,
8809        class_close_line,
8810        name,
8811    })
8812}
8813
8814/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
8815/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
8816///
8817/// The parser puts the namespace opener and the malformed function in one root
8818/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
8819/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
8820/// the malformed function must begin with an all-caps type, then an ERROR whose
8821/// sole identifier is `namespace`, followed by the inner namespace identifier
8822/// and a compound body; and that body must contain a complete named class or a
8823/// structurally fragmented class prefix. A text reparse cannot prove any of
8824/// those ownership boundaries.
8825fn cpp_nested_namespace_sentinel<'tree>(
8826    node: Node<'tree>,
8827    source: &str,
8828) -> Option<CppNestedNamespaceSentinel<'tree>> {
8829    if !node.has_error() {
8830        return None;
8831    }
8832
8833    let (function, mut namespace_components) = if node.kind() == "ERROR" {
8834        let mut cursor = node.walk();
8835        let functions = node
8836            .named_children(&mut cursor)
8837            .filter(|child| child.kind() == "function_definition")
8838            .collect::<Vec<_>>();
8839        let [function] = functions.as_slice() else {
8840            return None;
8841        };
8842        if !function.has_error() {
8843            return None;
8844        }
8845        let mut cursor = node.walk();
8846        let children = node.children(&mut cursor).collect::<Vec<_>>();
8847        let function_index = children
8848            .iter()
8849            .position(|child| same_node(*child, *function))?;
8850        let [outer_keyword, outer_name, outer_open] =
8851            children.get(function_index.checked_sub(3)?..function_index)?
8852        else {
8853            return None;
8854        };
8855        if outer_keyword.kind() != "namespace"
8856            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
8857            || outer_open.kind() != "{"
8858        {
8859            return None;
8860        }
8861        (
8862            *function,
8863            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
8864        )
8865    } else if node.kind() == "function_definition" {
8866        let declaration_list = node.parent()?;
8867        let namespace = declaration_list.parent()?;
8868        if declaration_list.kind() != "declaration_list"
8869            || namespace.kind() != "namespace_definition"
8870            || namespace.child_by_field_name("body") != Some(declaration_list)
8871        {
8872            return None;
8873        }
8874        (node, Vec::new())
8875    } else {
8876        return None;
8877    };
8878
8879    let mut cursor = function.walk();
8880    let named = function
8881        .named_children(&mut cursor)
8882        .filter(|child| child.kind() != "comment")
8883        .collect::<Vec<_>>();
8884    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
8885        return None;
8886    };
8887    if first_type.kind() != "type_identifier" {
8888        return None;
8889    }
8890    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
8891    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
8892        return None;
8893    }
8894    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
8895        return None;
8896    }
8897    let inner_keyword = inner_error.named_child(0)?;
8898    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
8899        return None;
8900    }
8901    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
8902        return None;
8903    }
8904    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
8905    if inner_name.is_empty() || body.kind() != "compound_statement" {
8906        return None;
8907    }
8908    namespace_components.push(inner_name);
8909
8910    let mut cursor = body.walk();
8911    let has_complete_class = body.named_children(&mut cursor).any(|child| {
8912        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
8913            cpp_body_node(class_node).is_some()
8914                && class_like_name(class_node, source)
8915                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
8916        })
8917    });
8918    if !has_complete_class && cpp_sentinel_fragmented_class_tail(function, *body, source).is_none()
8919    {
8920        return None;
8921    }
8922
8923    Some(CppNestedNamespaceSentinel {
8924        function,
8925        body: *body,
8926        namespace_components,
8927    })
8928}
8929
8930/// Recognize a namespace-begin sentinel directly beneath the translation unit.
8931///
8932/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
8933/// function whose type is the sentinel, whose declarator is the structured
8934/// qualified name `namespace::a::b`, and whose body contains the namespace
8935/// items. Declaration indexing already reparses this bounded region. The
8936/// inverse scanner retains the original tree, so recover the same namespace
8937/// components from the declarator fields for its lexical-scope metadata.
8938fn cpp_root_namespace_sentinel<'tree>(
8939    node: Node<'tree>,
8940    source: &str,
8941) -> Option<CppNestedNamespaceSentinel<'tree>> {
8942    if node.kind() != "function_definition"
8943        || !node.has_error()
8944        || node.parent()?.kind() != "translation_unit"
8945    {
8946        return None;
8947    }
8948    let first_type = node.child_by_field_name("type")?;
8949    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
8950    if first_type.kind() != "type_identifier"
8951        || sentinel.is_empty()
8952        || !cpp_export_macro_token(&sentinel)
8953    {
8954        return None;
8955    }
8956    let declarator = node.child_by_field_name("declarator")?;
8957    let body = node.child_by_field_name("body")?;
8958    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
8959        return None;
8960    }
8961    let mut cursor = node.walk();
8962    let named = node
8963        .named_children(&mut cursor)
8964        .filter(|child| child.kind() != "comment")
8965        .collect::<Vec<_>>();
8966    let [named_type, named_declarator, named_body] = named.as_slice() else {
8967        return None;
8968    };
8969    if !same_node(*named_type, first_type)
8970        || !same_node(*named_declarator, declarator)
8971        || !same_node(*named_body, body)
8972    {
8973        return None;
8974    }
8975    let mut declarator_components = Vec::new();
8976    let mut valid_components = true;
8977    walk_named_tree_preorder(declarator, true, |component| {
8978        if !matches!(
8979            component.kind(),
8980            "identifier" | "namespace_identifier" | "type_identifier"
8981        ) {
8982            return WalkControl::Continue;
8983        }
8984        let Some(component) = canonical_cpp_qualified_component(component, source) else {
8985            valid_components = false;
8986            return WalkControl::Break;
8987        };
8988        declarator_components.push(component.name);
8989        WalkControl::SkipChildren
8990    });
8991    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
8992        return None;
8993    }
8994    declarator_components.remove(0);
8995    let namespace_components = declarator_components;
8996    if namespace_components.is_empty()
8997        || namespace_components
8998            .iter()
8999            .any(|component| component.is_empty() || cpp_export_macro_token(component))
9000    {
9001        return None;
9002    }
9003
9004    let mut cursor = body.walk();
9005    let has_complete_class = body.named_children(&mut cursor).any(|child| {
9006        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9007            cpp_body_node(class_node).is_some()
9008                && class_like_name(class_node, source)
9009                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9010        })
9011    });
9012    if !has_complete_class && cpp_sentinel_fragmented_class_tail(node, body, source).is_none() {
9013        return None;
9014    }
9015
9016    Some(CppNestedNamespaceSentinel {
9017        function: node,
9018        body,
9019        namespace_components,
9020    })
9021}
9022
9023/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
9024/// malformed namespace-sentinel function.  The recovery is deliberately
9025/// structural: the class must be a direct body item, its own class node must be
9026/// erroneous and end before a unique anonymous `}` in the enclosing
9027/// declaration-list, and that namespace's next sibling must be a standalone
9028/// `;`.  The complete interior must pass the existing member-shaped reparse
9029/// gate. This avoids source brace scans and does not borrow a close from an
9030/// unrelated later declaration.
9031fn cpp_sentinel_fragmented_class_tail<'tree>(
9032    function: Node<'tree>,
9033    body: Node<'tree>,
9034    source: &str,
9035) -> Option<CppSentinelFragmentedClassTail<'tree>> {
9036    let mut cursor = body.walk();
9037    let candidates = body
9038        .named_children(&mut cursor)
9039        .filter_map(|child| {
9040            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
9041                let class_body = cpp_body_node(class_node)?;
9042                if !class_node.has_error() {
9043                    return None;
9044                }
9045                let name = class_like_name(class_node, source)?;
9046                let raw_supertypes =
9047                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
9048                        .then(|| extract_cpp_supertypes(class_node, source));
9049                return Some((
9050                    class_node,
9051                    template_node,
9052                    name,
9053                    class_body,
9054                    class_body.start_byte().checked_add(1)?,
9055                    raw_supertypes,
9056                ));
9057            }
9058            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
9059            Some((
9060                child,
9061                None,
9062                prefix.name,
9063                prefix.open,
9064                prefix.open.end_byte(),
9065                prefix.raw_supertypes,
9066            ))
9067        })
9068        .collect::<Vec<_>>();
9069    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
9070        candidates.as_slice()
9071    else {
9072        return None;
9073    };
9074    if name.is_empty() || cpp_export_macro_token(name) {
9075        return None;
9076    }
9077
9078    let (close, semicolon) =
9079        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
9080
9081    let reparse_end = close.start_byte();
9082    if *reparse_start >= reparse_end {
9083        return None;
9084    }
9085    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
9086    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9087        return None;
9088    }
9089    let class_range = Range {
9090        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9091        end_byte: semicolon.end_byte(),
9092        start_line: template_node.map_or(class_node.start_position().row, |node| {
9093            node.start_position().row
9094        }) + 1,
9095        end_line: semicolon.end_position().row + 1,
9096    };
9097    Some(CppSentinelFragmentedClassTail {
9098        class_node: *class_node,
9099        template_node: *template_node,
9100        name: name.clone(),
9101        raw_supertypes: raw_supertypes.clone(),
9102        fragmented: FragmentedExportBody {
9103            reparse_start: *reparse_start,
9104            reparse_end,
9105            class_range,
9106        },
9107        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9108    })
9109}
9110
9111/// Recover the class and out-of-line owner scopes from every malformed
9112/// namespace-sentinel region in `root`.
9113///
9114/// This is the shared structural counterpart to
9115/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
9116/// reuses the visitor's sentinel/class admission predicates instead of parsing
9117/// source text a second time.  The returned values own only ranges and names, so
9118/// they can be retained by an inverted usage scan after the tree borrow ends.
9119pub fn cpp_sentinel_recovered_classes(
9120    root: Node<'_>,
9121    source: &str,
9122) -> Vec<CppSentinelRecoveredClass> {
9123    if !root.has_error() {
9124        return Vec::new();
9125    }
9126    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
9127    let mut stack = vec![root];
9128    while let Some(current) = stack.pop() {
9129        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source)
9130            .or_else(|| cpp_root_namespace_sentinel(current, source))
9131        {
9132            let namespace_components = cpp_sentinel_recovered_namespace_components(
9133                recovered.function,
9134                &recovered.namespace_components,
9135                source,
9136            );
9137            let fragmented =
9138                cpp_sentinel_fragmented_class_tail(recovered.function, recovered.body, source);
9139            let mut class_candidates = Vec::new();
9140            let mut cursor = recovered.body.walk();
9141            for (class_node, template_node) in recovered
9142                .body
9143                .named_children(&mut cursor)
9144                .filter_map(cpp_sentinel_body_class_candidate)
9145            {
9146                let Some(name) = class_like_name(class_node, source) else {
9147                    continue;
9148                };
9149                if name.is_empty() || cpp_export_macro_token(&name) {
9150                    continue;
9151                }
9152                let is_fragmented = fragmented
9153                    .as_ref()
9154                    .is_some_and(|tail| same_node(tail.class_node, class_node));
9155                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
9156                    continue;
9157                }
9158                let class_range = if is_fragmented {
9159                    fragmented
9160                        .as_ref()
9161                        .map(|tail| tail.fragmented.class_range)
9162                        .expect("fragmented class range is present when class matches")
9163                } else {
9164                    cpp_declaration_range(template_node.unwrap_or(class_node))
9165                };
9166                class_candidates.push((class_range, name));
9167            }
9168            if let Some(fragmented) = fragmented
9169                .as_ref()
9170                .filter(|tail| tail.class_node.kind() == "ERROR")
9171            {
9172                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
9173            }
9174
9175            let mut owner_ranges =
9176                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
9177            cpp_sentinel_extend_unique_owner_ranges(
9178                &mut owner_ranges,
9179                cpp_sentinel_recovered_sibling_owner_ranges(
9180                    recovered.function,
9181                    &namespace_components,
9182                    source,
9183                ),
9184            );
9185            for (class_range, name) in class_candidates {
9186                push_cpp_sentinel_recovered_class(
9187                    &mut recovered_classes,
9188                    cpp_declaration_range(recovered.body),
9189                    &namespace_components,
9190                    class_range,
9191                    name,
9192                    &owner_ranges,
9193                );
9194            }
9195
9196            if let Some(declaration_list) = recovered
9197                .function
9198                .parent()
9199                .filter(|parent| parent.kind() == "declaration_list")
9200            {
9201                let outer_namespace =
9202                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
9203                push_cpp_sentinel_sibling_classes(
9204                    &mut recovered_classes,
9205                    declaration_list,
9206                    recovered.function,
9207                    &outer_namespace,
9208                    source,
9209                );
9210            }
9211        } else if let Some(region) = cpp_sentinel_macro_body_class_region(current, source) {
9212            let namespace_components = cpp_sentinel_recovered_namespace_components(
9213                current,
9214                &region.namespace_components,
9215                source,
9216            );
9217            let owner_container = current
9218                .parent()
9219                .filter(|parent| parent.kind() == "declaration_list")
9220                .unwrap_or(current);
9221            let owner_ranges =
9222                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9223            push_cpp_sentinel_recovered_class(
9224                &mut recovered_classes,
9225                cpp_declaration_range(owner_container),
9226                &namespace_components,
9227                Range {
9228                    start_byte: region.class_start,
9229                    end_byte: region.class_close_end,
9230                    start_line: region.class_start_line,
9231                    end_line: region.class_close_line,
9232                },
9233                region.name,
9234                &owner_ranges,
9235            );
9236        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
9237            // A generic sentinel-prefixed class can be reduced as a malformed
9238            // function/ERROR without the explicit `namespace X` token pair.
9239            // Reuse the declaration visitor's bounded reparse and retain only
9240            // the recovered class identity/range here.
9241            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
9242                region;
9243            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9244                continue;
9245            };
9246            let root = tree.root_node();
9247            let template_node = cpp_sentinel_reparsed_leading_template(root);
9248            let Some(reparsed_class) = cpp_sentinel_reparsed_class(root, template_node, source)
9249            else {
9250                continue;
9251            };
9252            let class_node = reparsed_class.declaration_node;
9253            let name = reparsed_class.name;
9254            let namespace_components =
9255                cpp_sentinel_recovered_namespace_components(current, &[], source);
9256            let owner_container = current
9257                .parent()
9258                .filter(|parent| parent.kind() == "declaration_list")
9259                .unwrap_or(current);
9260            let mut owner_ranges =
9261                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9262            cpp_sentinel_extend_unique_owner_ranges(
9263                &mut owner_ranges,
9264                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
9265            );
9266            push_cpp_sentinel_recovered_class(
9267                &mut recovered_classes,
9268                cpp_declaration_range(owner_container),
9269                &namespace_components,
9270                Range {
9271                    start_byte: class_start,
9272                    end_byte: close_end,
9273                    start_line: class_node.start_position().row + 1,
9274                    end_line: class_node.end_position().row + 1,
9275                },
9276                name,
9277                &owner_ranges,
9278            );
9279            if owner_container.kind() == "declaration_list" {
9280                push_cpp_sentinel_sibling_classes(
9281                    &mut recovered_classes,
9282                    owner_container,
9283                    current,
9284                    &namespace_components,
9285                    source,
9286                );
9287            }
9288        }
9289
9290        let mut cursor = current.walk();
9291        stack.extend(current.named_children(&mut cursor));
9292    }
9293    // A shallower sentinel can expose nested classes as apparent namespace
9294    // siblings even after a deeper sentinel proves that a containing class
9295    // owns their ranges. Drop those shadow descriptors; scope recovery starts
9296    // from the proven containing class and appends parser-visible class
9297    // ancestors, preserving the full `Outer::Inner` chain.
9298    let shadowed = recovered_classes
9299        .iter()
9300        .map(|candidate| {
9301            recovered_classes.iter().any(|container| {
9302                container.class_range.start_byte <= candidate.class_range.start_byte
9303                    && container.class_range.end_byte >= candidate.class_range.end_byte
9304                    && container.class_range != candidate.class_range
9305                    && container.namespace_scope_components.len()
9306                        > candidate.namespace_scope_components.len()
9307                    && container
9308                        .namespace_scope_components
9309                        .starts_with(&candidate.namespace_scope_components)
9310            })
9311        })
9312        .collect::<Vec<_>>();
9313    let mut index = 0usize;
9314    recovered_classes.retain(|_| {
9315        let keep = !shadowed[index];
9316        index += 1;
9317        keep
9318    });
9319    recovered_classes
9320}
9321
9322/// A flat sentinel can swallow the first class while leaving later classes and
9323/// their out-of-line definitions as ordinary declaration-list siblings.  Once
9324/// the malformed class proves the sentinel envelope, retain those structurally
9325/// complete sibling classes under the same surviving namespace so every member
9326/// owner in the region uses one recovery contract.
9327fn push_cpp_sentinel_sibling_classes(
9328    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9329    declaration_list: Node<'_>,
9330    sentinel_node: Node<'_>,
9331    namespace_components: &[String],
9332    source: &str,
9333) {
9334    let owner_ranges =
9335        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
9336    let namespace_range = cpp_declaration_range(declaration_list);
9337    let mut cursor = declaration_list.walk();
9338    for (class_node, template_node) in declaration_list
9339        .named_children(&mut cursor)
9340        .filter(|child| !same_node(*child, sentinel_node))
9341        .filter_map(cpp_sentinel_body_class_candidate)
9342    {
9343        let Some(name) = class_like_name(class_node, source) else {
9344            continue;
9345        };
9346        if name.is_empty()
9347            || cpp_export_macro_token(&name)
9348            || cpp_complete_class_body_close(class_node).is_none()
9349        {
9350            continue;
9351        }
9352        push_cpp_sentinel_recovered_class(
9353            recovered_classes,
9354            namespace_range,
9355            namespace_components,
9356            cpp_declaration_range(template_node.unwrap_or(class_node)),
9357            name,
9358            &owner_ranges,
9359        );
9360    }
9361}
9362
9363fn push_cpp_sentinel_recovered_class(
9364    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9365    namespace_range: Range,
9366    namespace_components: &[String],
9367    class_range: Range,
9368    name: String,
9369    owner_ranges: &[CppSentinelRecoveredOwner],
9370) {
9371    let mut scope_components = namespace_components.to_vec();
9372    scope_components.push(name);
9373    let owner_ranges = owner_ranges
9374        .iter()
9375        .filter(|owner| owner.scope_components.starts_with(&scope_components))
9376        .cloned()
9377        .collect::<Vec<_>>();
9378    if recovered_classes.iter().any(|existing| {
9379        existing.class_range == class_range && existing.scope_components == scope_components
9380    }) {
9381        return;
9382    }
9383    recovered_classes.push(CppSentinelRecoveredClass {
9384        namespace_range,
9385        namespace_scope_components: namespace_components.to_vec(),
9386        class_range,
9387        scope_components,
9388        owner_ranges,
9389    });
9390}
9391
9392fn cpp_sentinel_recovered_namespace_components(
9393    function: Node<'_>,
9394    recovered_components: &[String],
9395    source: &str,
9396) -> Vec<String> {
9397    let mut ancestor_components = Vec::new();
9398    let mut ancestor = function.parent();
9399    while let Some(current) = ancestor {
9400        if current.kind() == "namespace_definition"
9401            && let Some(name_node) = current.child_by_field_name("name")
9402            && let Some(components) = cpp_name_components(name_node, source)
9403        {
9404            ancestor_components.push(
9405                components
9406                    .into_iter()
9407                    .map(|component| component.name)
9408                    .collect::<Vec<_>>(),
9409            );
9410        }
9411        ancestor = current.parent();
9412    }
9413    ancestor_components.reverse();
9414    let mut ancestors = ancestor_components
9415        .into_iter()
9416        .flatten()
9417        .collect::<Vec<_>>();
9418
9419    let overlap = (0..=ancestors.len().min(recovered_components.len()))
9420        .rev()
9421        .find(|length| {
9422            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
9423        })
9424        .unwrap_or(0);
9425    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
9426    ancestors
9427}
9428
9429fn cpp_sentinel_recovered_owner_ranges(
9430    body: Node<'_>,
9431    namespace_components: &[String],
9432    source: &str,
9433) -> Vec<CppSentinelRecoveredOwner> {
9434    let mut owners = Vec::new();
9435    walk_named_tree_preorder(body, true, |node| {
9436        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9437    });
9438    owners
9439}
9440
9441fn cpp_sentinel_collect_owner_range(
9442    node: Node<'_>,
9443    namespace_components: &[String],
9444    source: &str,
9445    owners: &mut Vec<CppSentinelRecoveredOwner>,
9446) -> WalkControl {
9447    if node.kind() != "function_definition" {
9448        return WalkControl::Continue;
9449    }
9450    let Some(function_declarator) = extract_function_declarator(node) else {
9451        return WalkControl::Continue;
9452    };
9453    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
9454        return WalkControl::Continue;
9455    };
9456    let Some(mut components) = cpp_name_components(name_node, source) else {
9457        return WalkControl::Continue;
9458    };
9459    if components.len() <= 1 {
9460        return WalkControl::Continue;
9461    }
9462    components.pop();
9463    let mut owner_components = components
9464        .into_iter()
9465        .map(|component| component.name)
9466        .collect::<Vec<_>>();
9467    let overlap = (0..=namespace_components.len().min(owner_components.len()))
9468        .rev()
9469        .find(|length| {
9470            owner_components[..*length]
9471                == namespace_components[namespace_components.len().saturating_sub(*length)..]
9472        })
9473        .unwrap_or(0);
9474    let mut scope_components = namespace_components.to_vec();
9475    scope_components.extend(owner_components.drain(overlap..));
9476    if scope_components.len() <= namespace_components.len() {
9477        return WalkControl::Continue;
9478    }
9479    let range = cpp_declaration_range(node);
9480    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
9481        existing.range == range && existing.scope_components == scope_components
9482    }) {
9483        owners.push(CppSentinelRecoveredOwner {
9484            range,
9485            owner_name_start_byte: name_node.start_byte(),
9486            namespace_component_count: namespace_components.len(),
9487            scope_components,
9488        });
9489    }
9490    WalkControl::Continue
9491}
9492
9493fn cpp_sentinel_extend_unique_owner_ranges(
9494    owners: &mut Vec<CppSentinelRecoveredOwner>,
9495    additional: Vec<CppSentinelRecoveredOwner>,
9496) {
9497    for owner in additional {
9498        if !owners.iter().any(|existing| {
9499            existing.range == owner.range && existing.scope_components == owner.scope_components
9500        }) {
9501            owners.push(owner);
9502        }
9503    }
9504}
9505
9506fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
9507    if node.kind() != "ERROR" || node.named_child_count() != 1 {
9508        return false;
9509    }
9510    let Some(end_name) = node.named_child(0) else {
9511        return false;
9512    };
9513    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
9514        return false;
9515    }
9516    let mut cursor = node.walk();
9517    node.children(&mut cursor)
9518        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
9519}
9520
9521/// Collect owner definitions that the malformed sentinel left as later
9522/// declaration-list siblings. Parser-visible namespace siblings are a hard
9523/// boundary: their declarations must keep their own lexical namespace.
9524fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9525    parent: Node<'_>,
9526    sentinel_node: Node<'_>,
9527    namespace_components: &[String],
9528    source: &str,
9529) -> Vec<CppSentinelRecoveredOwner> {
9530    let mut owners = Vec::new();
9531    let mut after_sentinel = false;
9532    let mut cursor = parent.walk();
9533    for child in parent.named_children(&mut cursor) {
9534        if !after_sentinel {
9535            if same_node(child, sentinel_node) {
9536                after_sentinel = true;
9537            }
9538            continue;
9539        }
9540        walk_named_tree_preorder(child, true, |node| {
9541            if node.kind() == "namespace_definition" {
9542                return WalkControl::SkipChildren;
9543            }
9544            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9545        });
9546    }
9547    owners
9548}
9549
9550/// Collect owner definitions after a malformed namespace, stopping only at
9551/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
9552/// enclosing container is not trusted to belong to the recovered namespace.
9553fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9554    parent: Node<'_>,
9555    sentinel_node: Node<'_>,
9556    namespace_components: &[String],
9557    source: &str,
9558) -> Option<Vec<CppSentinelRecoveredOwner>> {
9559    let mut owners = Vec::new();
9560    let mut after_namespace = false;
9561    let mut cursor = parent.walk();
9562    for child in parent.named_children(&mut cursor) {
9563        if !after_namespace {
9564            if same_node(child, sentinel_node) {
9565                after_namespace = true;
9566            }
9567            continue;
9568        }
9569        if cpp_sentinel_namespace_end(child, source) {
9570            return Some(owners);
9571        }
9572        walk_named_tree_preorder(child, true, |node| {
9573            if node.kind() == "namespace_definition" {
9574                return WalkControl::SkipChildren;
9575            }
9576            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9577        });
9578    }
9579    None
9580}
9581
9582fn cpp_sentinel_recovered_sibling_owner_ranges(
9583    sentinel_node: Node<'_>,
9584    namespace_components: &[String],
9585    source: &str,
9586) -> Vec<CppSentinelRecoveredOwner> {
9587    let Some(declaration_list) = sentinel_node
9588        .parent()
9589        .filter(|parent| parent.kind() == "declaration_list")
9590    else {
9591        return Vec::new();
9592    };
9593    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9594        declaration_list,
9595        sentinel_node,
9596        namespace_components,
9597        source,
9598    );
9599
9600    let Some(namespace) = declaration_list
9601        .parent()
9602        .filter(|parent| parent.kind() == "namespace_definition")
9603    else {
9604        return owners;
9605    };
9606    let Some(outer_parent) = namespace.parent() else {
9607        return owners;
9608    };
9609    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9610        outer_parent,
9611        namespace,
9612        namespace_components,
9613        source,
9614    ) {
9615        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9616    }
9617    owners
9618}
9619
9620fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9621    let mut current = function_declarator.child_by_field_name("declarator")?;
9622    loop {
9623        if matches!(
9624            current.kind(),
9625            "qualified_identifier"
9626                | "scoped_identifier"
9627                | "scoped_type_identifier"
9628                | "identifier"
9629                | "field_identifier"
9630                | "operator_name"
9631                | "destructor_name"
9632                | "literal_operator_name"
9633        ) {
9634            return Some(current);
9635        }
9636        current = current
9637            .child_by_field_name("declarator")
9638            .or_else(|| current.child_by_field_name("name"))
9639            .or_else(|| last_named_child(current))?;
9640    }
9641}
9642
9643fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
9644    match node.kind() {
9645        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
9646            let mut components = match node.child_by_field_name("scope") {
9647                Some(scope) => cpp_name_components(scope, source)?,
9648                None => Vec::new(),
9649            };
9650            let name = node.child_by_field_name("name")?;
9651            components.push(canonical_cpp_qualified_component(name, source)?);
9652            Some(components)
9653        }
9654        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
9655    }
9656}
9657
9658fn cpp_sentinel_fragment_boundary<'tree>(
9659    function: Node<'tree>,
9660    class_node: Node<'tree>,
9661    class_body: Node<'tree>,
9662    source: &str,
9663) -> Option<(Node<'tree>, Node<'tree>)> {
9664    let declaration_list = function.parent()?;
9665    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
9666        return None;
9667    }
9668    let namespace = declaration_list.parent()?;
9669    if namespace.kind() != "namespace_definition"
9670        || namespace.child_by_field_name("body") != Some(declaration_list)
9671    {
9672        return None;
9673    }
9674    let mut cursor = declaration_list.walk();
9675    let closes = declaration_list
9676        .children(&mut cursor)
9677        .filter(|child| {
9678            !child.is_named()
9679                && child.kind() == "}"
9680                && child.start_byte() >= function.end_byte()
9681                && child.start_byte() > class_node.end_byte()
9682                && child.start_byte() > class_body.start_byte()
9683        })
9684        .collect::<Vec<_>>();
9685    let [close] = closes.as_slice() else {
9686        return None;
9687    };
9688    let semicolon = namespace.next_named_sibling()?;
9689    if !cpp_is_stray_semicolon(semicolon, source)
9690        || close.end_byte() != namespace.end_byte()
9691        || semicolon.start_byte() < namespace.end_byte()
9692    {
9693        return None;
9694    }
9695    Some((*close, semicolon))
9696}
9697
9698/// Detect the bogus declaration/function tree that tree-sitter recovers for a
9699/// region prefixed by an object-like macro sentinel the parser cannot see
9700/// (issue #941), and return the byte range `[start, end)` of the swallowed
9701/// declaration interior to reparse.
9702///
9703/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
9704/// `function_definition` whose first non-comment named child is the sentinel
9705/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
9706/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
9707/// the real items.
9708/// `start` is the end of the sentinel identifier -- everything after it is the
9709/// genuine source. `end` is the node's end, extended across any trailing empty
9710/// `;` statement the mis-parse displaced past the node (the class/struct closing
9711/// semicolon), so the reparse sees a complete, brace-balanced item.
9712///
9713/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
9714/// node (`has_error`). Unknown annotation/export macros can make a real callable
9715/// error-recovered even though tree-sitter still preserves its declarator, so a
9716/// preserved callable is admitted only when a displaced class keyword precedes
9717/// that declarator. The clean-reparse-to-items gate in
9718/// `cpp_reparsed_items_are_indexable` is the final arbiter.
9719/// Return the reparse start and, when present, the structurally recovered class
9720/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
9721/// separately from the reparse start because an opaque template-declaration
9722/// macro may precede it.
9723fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
9724    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
9725    {
9726        return None;
9727    }
9728    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
9729    // retain a valid function declarator despite the unknown export macro making
9730    // the outer node erroneous. Remember that declarator for the ordering gate
9731    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
9732    // class keyword precedes a spurious callable assembled from a later member.
9733    let mut declarator_cursor = node.walk();
9734    let preserved_callable = node
9735        .children_by_field_name("declarator", &mut declarator_cursor)
9736        .find_map(extract_function_declarator);
9737    // Leading documentation comments are attached to the malformed
9738    // `function_definition` as named children.  They are not part of the
9739    // sentinel prefix, so select the first non-comment child structurally
9740    // rather than requiring the sentinel to be child zero.  This is the shape
9741    // emitted for nlohmann/json's `basic_json`: its class documentation comment
9742    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
9743    // envelope otherwise ends at the first nested union.
9744    let mut cursor = node.walk();
9745    let first = node
9746        .named_children(&mut cursor)
9747        .find(|child| child.kind() != "comment")?;
9748    if first.kind() != "type_identifier" {
9749        return None;
9750    }
9751    let sentinel = normalize_cpp_whitespace(node_text(first, source));
9752    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9753        return None;
9754    }
9755    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
9756    // makes the trailing sentinel of one region and the leading sentinel of the
9757    // next both land as bare macro-token identifiers ahead of the real content.
9758    // Advance past every leading macro-token identifier so the reparse begins at
9759    // genuine source rather than another sentinel that would re-form the bogus
9760    // shape and fail the reparse gate.
9761    let mut start = first.end_byte();
9762    let mut after_first = false;
9763    let mut cursor = node.walk();
9764    for child in node.named_children(&mut cursor) {
9765        if !after_first {
9766            if same_node(child, first) {
9767                after_first = true;
9768            }
9769            continue;
9770        }
9771        if matches!(child.kind(), "identifier" | "type_identifier")
9772            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
9773        {
9774            start = child.end_byte();
9775        } else {
9776            break;
9777        }
9778    }
9779    // An additional opaque template-declaration macro before a class can be
9780    // folded into the bogus function's qualified declarator.  In that shape
9781    // the macro is not a direct sibling we can skip above; tree-sitter exposes
9782    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
9783    // Reparse from that keyword (or a real preceding `template` keyword) so the
9784    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
9785    // a class nested in a genuine sentinel-wrapped namespace lies after the
9786    // body opening and must not change the established region start.
9787    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
9788    let mut class_start = None;
9789    let mut template_start = None;
9790    let mut stack = vec![node];
9791    while let Some(current) = stack.pop() {
9792        if current.start_byte() >= prefix_end {
9793            continue;
9794        }
9795        if matches!(
9796            current.kind(),
9797            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
9798        ) {
9799            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
9800                "class" | "struct" | "union" | "enum" => {
9801                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
9802                        seen.min(current.start_byte())
9803                    }));
9804                }
9805                "template" => {
9806                    template_start =
9807                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
9808                            seen.min(current.start_byte())
9809                        }));
9810                }
9811                _ => {}
9812            }
9813        }
9814        let mut cursor = current.walk();
9815        stack.extend(current.children(&mut cursor));
9816    }
9817    if preserved_callable.is_some_and(|callable| {
9818        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
9819    }) {
9820        return None;
9821    }
9822    if let Some(class_start) = class_start {
9823        start = template_start
9824            .filter(|template_start| *template_start < class_start)
9825            .unwrap_or(class_start);
9826    }
9827    Some((start, class_start))
9828}
9829
9830/// Locate a sentinel-prefixed class whose malformed declaration was split across
9831/// root-level siblings. The true class close is represented structurally as a
9832/// lone `}` error followed by the class's displaced `;`; nested method/body
9833/// errors are not direct siblings of the sentinel node and therefore cannot
9834/// satisfy this pair.
9835fn cpp_sentinel_macro_class_region(
9836    node: Node<'_>,
9837    source: &str,
9838) -> Option<(usize, usize, usize, usize, usize, usize)> {
9839    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
9840        return None;
9841    };
9842    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
9843        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
9844        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
9845    if class_start >= body_open_start {
9846        return None;
9847    }
9848    let sibling_close = {
9849        let mut sibling = node.next_named_sibling();
9850        let mut found = None;
9851        while let Some(current) = sibling {
9852            let next = current.next_named_sibling();
9853            if cpp_is_stray_close_brace(current, source)
9854                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9855            {
9856                let semicolon = next.expect("checked above");
9857                found = Some((
9858                    current.start_byte(),
9859                    semicolon.end_byte(),
9860                    semicolon.end_position().row + 1,
9861                ));
9862                break;
9863            }
9864            sibling = next;
9865        }
9866        found
9867    };
9868    // A stray `};` sibling is this class's close only when the bounded reparse
9869    // agrees the first body-bearing class ENDS there. When the malformed
9870    // envelope swallowed the class's true close, the scan can promote a much
9871    // later scope's close instead -- in protobuf-generated headers
9872    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
9873    // `struct TableStruct_*` paired with the first message class's `};`, making
9874    // the recovered "class body" span whole `namespace {}` blocks and minting
9875    // namespace-scope classes as nested members of the recovered class, which
9876    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
9877    // (#2275). On disagreement, fall through to the suffix-reparse boundary
9878    // below, which derives the close from the class node's own balanced body
9879    // range.
9880    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
9881        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9882            return false;
9883        };
9884        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9885        let Some(reparsed_class) =
9886            cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)
9887        else {
9888            return false;
9889        };
9890        let body = reparsed_class.body;
9891        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
9892    });
9893    let (class_close_start, class_close_end, class_close_line) =
9894        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
9895            (class_close_start, class_close_end, class_close_line)
9896        } else {
9897            // When the malformed envelope itself is an ERROR, tree-sitter can
9898            // leave the class's balanced close in the source while promoting
9899            // all following members to siblings. Reparse the complete suffix
9900            // and use the first body-bearing class node's own field range as
9901            // the partition boundary. This keeps balancing in tree-sitter and
9902            // preserves the source's original byte offsets.
9903            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
9904            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
9905            let reparsed_class =
9906                cpp_sentinel_reparsed_class(tree.root_node(), template_node, source)?;
9907            let body = reparsed_class.body;
9908            let class_close_end = body.end_byte();
9909            let class_close_start = class_close_end.checked_sub(1)?;
9910            let class_close_line = body.end_position().row + 1;
9911            (class_close_start, class_close_end, class_close_line)
9912        };
9913    if class_close_start <= class_start {
9914        return None;
9915    }
9916
9917    // Reparse only far enough to expose the class body opening. This is a
9918    // structured check that the candidate really begins with a body-bearing
9919    // class-like item; the original malformed tree cannot provide that node.
9920    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9921    let class_root = tree.root_node();
9922    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9923    let reparsed_class = cpp_sentinel_reparsed_class(class_root, template_node, source)?;
9924    let body = reparsed_class.body;
9925    // The class body opening must agree with the malformed wrapper's structured
9926    // body field. This rejects an inner nested class while permitting later
9927    // members to remain fragmented as root-level siblings in the bounded parse.
9928    if body.start_byte() != body_open_start {
9929        return None;
9930    }
9931    let body_start = body.start_byte().checked_add(1)?;
9932    (body_start < class_close_start).then_some((
9933        reparse_start,
9934        class_start,
9935        body_start,
9936        class_close_start,
9937        class_close_end,
9938        class_close_line,
9939    ))
9940}
9941
9942/// Find the `{` token immediately following the class/struct/union/enum token
9943/// at `class_start` in the malformed tree. The token is anonymous in the C++
9944/// grammar, so this deliberately walks all children (not only named children)
9945/// and relies on sibling structure rather than source-text searching.
9946fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
9947    let mut stack = vec![node];
9948    while let Some(current) = stack.pop() {
9949        if current.start_byte() == class_start
9950            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
9951        {
9952            let mut sibling = current.next_sibling();
9953            while let Some(candidate) = sibling {
9954                if candidate.kind() == "{" {
9955                    return Some(candidate.start_byte());
9956                }
9957                sibling = candidate.next_sibling();
9958            }
9959        }
9960        let mut cursor = current.walk();
9961        stack.extend(current.children(&mut cursor));
9962    }
9963    None
9964}
9965
9966/// The class body that tree-sitter displaced out of a sentinel-prefixed
9967/// declaration and left as the malformed node's next sibling.
9968///
9969/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
9970/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
9971/// the last child of that `ERROR` and its `{` opens a sibling
9972/// `compound_statement` instead. The body is still the malformed tree's own
9973/// structured token, which is what the caller's `body.start_byte() !=
9974/// body_open_start` agreement check needs; it just is not reachable by walking
9975/// forward from the class token inside the node.
9976fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
9977    node.next_named_sibling()
9978        .filter(|sibling| sibling.kind() == "compound_statement")
9979}
9980
9981fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
9982    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
9983    let mut end = if class_start.is_some() {
9984        cpp_macro_prefixed_class_end(source, start)?
9985    } else {
9986        node.end_byte()
9987    };
9988    if class_start.is_none()
9989        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
9990    {
9991        end = end.max(namespace_end);
9992    }
9993    let mut sibling = node.next_named_sibling();
9994    while let Some(current) = sibling {
9995        if !cpp_is_stray_semicolon(current, source) {
9996            break;
9997        }
9998        end = current.end_byte();
9999        sibling = current.next_named_sibling();
10000    }
10001    (start < end).then_some((start, end))
10002}
10003
10004/// Extend a sentinel reparse through a following namespace that tree-sitter
10005/// flattened into the sentinel node's sibling list.
10006///
10007/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
10008/// unknown macro becomes a false function return type and consumes the first
10009/// namespace body. A second `namespace detail` then loses its enclosing node:
10010/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
10011/// attaches its declarations to the surrounding error tree. Reparse from that
10012/// structured keyword so tree-sitter, rather than a source-text brace scan,
10013/// supplies the complete namespace boundary.
10014fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
10015    let mut sibling = node.next_sibling();
10016    let keyword = loop {
10017        let candidate = sibling?;
10018        sibling = candidate.next_sibling();
10019        if candidate.kind() != "comment" {
10020            break candidate;
10021        }
10022    };
10023    if keyword.kind() != "namespace" {
10024        return None;
10025    }
10026    let name = loop {
10027        let candidate = sibling?;
10028        sibling = candidate.next_sibling();
10029        if candidate.kind() != "comment" {
10030            break candidate;
10031        }
10032    };
10033    if cpp_namespace_name_components(name, source).is_empty() {
10034        return None;
10035    }
10036    let open = loop {
10037        let candidate = sibling?;
10038        sibling = candidate.next_sibling();
10039        if candidate.kind() != "comment" {
10040            break candidate;
10041        }
10042    };
10043    if open.kind() != "{" {
10044        return None;
10045    }
10046
10047    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
10048    let root = tree.root_node();
10049    let mut cursor = root.walk();
10050    let namespace = root
10051        .named_children(&mut cursor)
10052        .find(|candidate| candidate.kind() != "comment")?;
10053    (namespace.kind() == "namespace_definition"
10054        && namespace.start_byte() == keyword.start_byte()
10055        && namespace.child_by_field_name("body").is_some())
10056    .then_some(namespace.end_byte())
10057}
10058
10059/// Parse the source suffix beginning at a structurally recovered class/template
10060/// keyword and return the end of its first body-bearing class item.  The parser,
10061/// rather than a brace scanner, owns nested-body balancing.  This is needed when
10062/// the original error tree truncates the class and scatters later members as
10063/// top-level siblings.
10064fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
10065    let tree = cpp_reparse_region_items(source, start, source.len())?;
10066    let root = tree.root_node();
10067    let mut cursor = root.walk();
10068    for item in root.named_children(&mut cursor) {
10069        if item.end_byte() <= start || item.kind() == "comment" {
10070            continue;
10071        }
10072        let mut stack = vec![item];
10073        while let Some(current) = stack.pop() {
10074            if matches!(
10075                current.kind(),
10076                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10077            ) && cpp_body_node(current).is_some()
10078            {
10079                return Some(current.end_byte());
10080            }
10081            let mut cursor = current.walk();
10082            stack.extend(current.named_children(&mut cursor));
10083        }
10084        // The recovered prefix is required to begin with the class item.  If
10085        // the first real item is something else, fail closed rather than skip
10086        // arbitrary source looking for a later class.
10087        return None;
10088    }
10089    None
10090}
10091
10092/// An empty `;` statement: the displaced closing semicolon of a struct/class that
10093/// the sentinel mis-parse split off past the bogus function node.
10094fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
10095    node.kind() == "expression_statement"
10096        && node.named_child_count() == 0
10097        && node_text(node, source).trim() == ";"
10098}
10099
10100/// Recover the real field name when a leading object-like annotation macro
10101/// displaces a qualified type into tree-sitter's bit-field recovery shape.
10102///
10103/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
10104/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
10105/// `bitfield_clause` containing an error plus an assignment.  The assignment's
10106/// left field is the only structured declaration name in that malformed tail.
10107/// A real bit-field is excluded by the all-caps macro type and required error.
10108fn recovered_macro_qualified_field_declarators<'tree>(
10109    node: Node<'tree>,
10110    source: &str,
10111) -> Option<Vec<Node<'tree>>> {
10112    if node.kind() != "field_declaration" {
10113        return None;
10114    }
10115    let macro_type = node.child_by_field_name("type")?;
10116    if macro_type.kind() != "type_identifier"
10117        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10118    {
10119        return None;
10120    }
10121    let pseudo_declarator = node.child_by_field_name("declarator")?;
10122    if pseudo_declarator.kind() != "field_identifier" {
10123        return None;
10124    }
10125    let mut cursor = node.walk();
10126    let clause = node
10127        .named_children(&mut cursor)
10128        .find(|child| child.kind() == "bitfield_clause")?;
10129    if !(0..clause.named_child_count()).any(|index| {
10130        clause
10131            .named_child(index)
10132            .is_some_and(|child| child.kind() == "ERROR")
10133    }) {
10134        return None;
10135    }
10136    let mut recovered = Vec::new();
10137    let mut stack = vec![clause];
10138    while let Some(current) = stack.pop() {
10139        if current.kind() == "assignment_expression"
10140            && let Some(left) = current.child_by_field_name("left")
10141            && extract_variable_name(left, source).is_some()
10142        {
10143            recovered.push(left);
10144            break;
10145        }
10146        let mut cursor = current.walk();
10147        stack.extend(current.named_children(&mut cursor));
10148    }
10149    if recovered.is_empty() {
10150        return None;
10151    }
10152    let mut cursor = node.walk();
10153    recovered.extend(
10154        node.children_by_field_name("declarator", &mut cursor)
10155            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
10156    );
10157    Some(recovered)
10158}
10159
10160/// Recover a macro-qualified constructor that tree-sitter represents as one
10161/// field declaration. The constructor call remains inside the direct recovery
10162/// error, while each member initializer becomes a false function declarator.
10163/// The class owner proves the constructor name and lets the caller ignore those
10164/// initializer declarators.
10165fn recovered_macro_qualified_constructor_call<'tree>(
10166    node: Node<'tree>,
10167    class_name: &str,
10168    source: &str,
10169) -> Option<Node<'tree>> {
10170    if node.kind() != "field_declaration" {
10171        return None;
10172    }
10173    let macro_type = node.child_by_field_name("type")?;
10174    if macro_type.kind() != "type_identifier"
10175        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10176    {
10177        return None;
10178    }
10179    let mut cursor = node.walk();
10180    let bitfield = node
10181        .named_children(&mut cursor)
10182        .find(|child| child.kind() == "bitfield_clause")?;
10183    let error = bitfield
10184        .named_child(0)
10185        .filter(|child| child.kind() == "ERROR")?;
10186    let mut stack = vec![error];
10187    while let Some(current) = stack.pop() {
10188        if current.kind() == "call_expression"
10189            && current
10190                .child_by_field_name("function")
10191                .is_some_and(|function| node_text(function, source) == class_name)
10192            && current
10193                .child_by_field_name("arguments")
10194                .is_some_and(|arguments| arguments.kind() == "argument_list")
10195        {
10196            return Some(current);
10197        }
10198        let mut cursor = current.walk();
10199        stack.extend(current.named_children(&mut cursor));
10200    }
10201    None
10202}
10203
10204/// Recover a macro-qualified member function declaration that tree-sitter
10205/// represents as a pseudo-field. An object-like export macro before a qualified
10206/// return type can displace the namespace and type into an ERROR/bitfield
10207/// recovery, leaving the callable as a structured `call_expression`.
10208///
10209/// The caller must route this shape before ordinary declarator classification;
10210/// otherwise the displaced namespace identifier is published as a field.
10211fn recovered_macro_qualified_function_call<'tree>(
10212    node: Node<'tree>,
10213    source: &str,
10214) -> Option<Node<'tree>> {
10215    if node.kind() != "field_declaration" {
10216        return None;
10217    }
10218    let macro_type = node.child_by_field_name("type")?;
10219    if macro_type.kind() != "type_identifier"
10220        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10221    {
10222        return None;
10223    }
10224    let declarator = node.child_by_field_name("declarator")?;
10225    if declarator.kind() != "field_identifier" {
10226        return None;
10227    }
10228    let mut cursor = node.walk();
10229    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10230    if !named.iter().any(|child| {
10231        child.kind() == "storage_class_specifier"
10232            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
10233    }) {
10234        return None;
10235    }
10236    let bitfield = named
10237        .iter()
10238        .find(|child| child.kind() == "bitfield_clause")?;
10239    let mut bitfield_cursor = bitfield.walk();
10240    let payload = bitfield
10241        .named_children(&mut bitfield_cursor)
10242        .collect::<Vec<_>>();
10243    let [displaced_error, call] = payload.as_slice() else {
10244        return None;
10245    };
10246    if displaced_error.kind() != "ERROR"
10247        || displaced_error.named_child_count() != 1
10248        || displaced_error
10249            .named_child(0)
10250            .is_none_or(|child| child.kind() != "identifier")
10251        || call.kind() != "call_expression"
10252        || call
10253            .child_by_field_name("function")
10254            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
10255        || call
10256            .child_by_field_name("arguments")
10257            .is_none_or(|arguments| arguments.kind() != "argument_list")
10258    {
10259        return None;
10260    }
10261    Some(*call)
10262}
10263
10264fn recovered_macro_qualified_function_parameters(
10265    arguments: Node<'_>,
10266    source: &str,
10267) -> Option<(String, Vec<String>)> {
10268    if arguments.kind() != "argument_list" {
10269        return None;
10270    }
10271    let mut cursor = arguments.walk();
10272    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
10273    if named.is_empty() {
10274        return Some(("()".to_string(), Vec::new()));
10275    }
10276    let mut types = Vec::new();
10277    let mut labels = Vec::new();
10278    let mut index = 0;
10279    while index < named.len() {
10280        let parameter_type = named[index];
10281        let parameter_name = named.get(index + 1).copied()?;
10282        if !matches!(
10283            parameter_type.kind(),
10284            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
10285        ) || parameter_name.kind() != "ERROR"
10286            || parameter_name.named_child_count() != 1
10287            || parameter_name
10288                .named_child(0)
10289                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
10290        {
10291            return None;
10292        }
10293        let parameter_name = parameter_name.named_child(0)?;
10294        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
10295        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
10296        index += 2;
10297    }
10298    Some((format!("({})", types.join(", ")), labels))
10299}
10300
10301/// Recognize the phantom field tree-sitter emits for a macro-qualified
10302/// function return type.  For example,
10303/// `static API result_type ThresholdForSmallA() { ... }` can become a
10304/// `field_declaration` (`API` as the type and `result_type` as a field name)
10305/// followed by a clean `function_definition` for `ThresholdForSmallA`.
10306///
10307/// Keep this predicate entirely tied to the CST envelope: the type must be an
10308/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
10309/// the declaration must carry a missing semicolon rather than a real one, and
10310/// the immediate named sibling must expose a function declarator.  A real
10311/// macro-decorated field with an explicit semicolon therefore remains a field.
10312pub fn recovered_macro_return_type_node<'tree>(
10313    node: Node<'tree>,
10314    source: &str,
10315) -> Option<Node<'tree>> {
10316    if node.kind() != "field_declaration" {
10317        return None;
10318    }
10319    let macro_type = node.child_by_field_name("type")?;
10320    if macro_type.kind() != "type_identifier"
10321        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10322    {
10323        return None;
10324    }
10325    let declarator = node.child_by_field_name("declarator")?;
10326    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
10327        return None;
10328    }
10329    let mut has_missing_semicolon = false;
10330    let mut has_real_semicolon = false;
10331    for index in 0..node.child_count() {
10332        let Some(child) = node.child(index) else {
10333            continue;
10334        };
10335        if child.kind() != ";" {
10336            continue;
10337        }
10338        if child.is_missing() {
10339            has_missing_semicolon = true;
10340        } else {
10341            has_real_semicolon = true;
10342        }
10343    }
10344    if !has_missing_semicolon || has_real_semicolon {
10345        return None;
10346    }
10347    let mut next = node.next_named_sibling();
10348    while next.is_some_and(|sibling| sibling.kind() == "comment") {
10349        next = next.and_then(|sibling| sibling.next_named_sibling());
10350    }
10351    let next = next?;
10352    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
10353        return None;
10354    }
10355    let function_declarator = next.child_by_field_name("declarator")?;
10356    extract_function_declarator(function_declarator).map(|_| declarator)
10357}
10358
10359/// Whether `name` is a type parameter of a template declaration that lexically
10360/// encloses `node`. The malformed macro-return field uses the parameter name as
10361/// its pseudo-declarator; preserving that field is necessary to publish a
10362/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
10363/// ancestors instead of interpreting source text so nested templates and
10364/// parser-recovered regions retain their real lexical scopes.
10365pub(crate) fn cpp_active_template_type_parameter(node: Node<'_>, name: &str, source: &str) -> bool {
10366    let mut ancestor = node.parent();
10367    while let Some(current) = ancestor {
10368        if current.kind() == "template_declaration"
10369            && let Some(parameters) = current.child_by_field_name("parameters")
10370        {
10371            let mut cursor = parameters.walk();
10372            if parameters.named_children(&mut cursor).any(|parameter| {
10373                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
10374                    && cpp_template_parameter_name(parameter, source)
10375                        .is_some_and(|parameter_name| parameter_name == name)
10376            }) {
10377                return true;
10378            }
10379        }
10380        ancestor = current.parent();
10381    }
10382    false
10383}
10384
10385/// Reparse the region `[start, end)` of `source` as C++, confined to the region
10386/// via included ranges so every reparsed node keeps its original byte offset and
10387/// line number. The existing visitors read node text from the original source,
10388/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
10389/// `parse_rust_region_tree` technique.
10390fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
10391    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
10392}
10393
10394fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
10395    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
10396        return None;
10397    }
10398    let semicolon = node.next_sibling()?;
10399    if semicolon.kind() != ";" || semicolon.is_missing() {
10400        return None;
10401    }
10402    let row = node.start_position().row;
10403    let mut start = node.start_byte();
10404    let mut sibling = node.prev_sibling();
10405    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
10406        if previous.kind() == ";" {
10407            break;
10408        }
10409        start = previous.start_byte();
10410        sibling = previous.prev_sibling();
10411    }
10412    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
10413}
10414
10415fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
10416    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
10417        return false;
10418    }
10419    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
10420        return false;
10421    }
10422    let Some(declarator) = (if node.kind() == "function_definition" {
10423        node.child_by_field_name("declarator")
10424            .and_then(extract_function_declarator)
10425    } else {
10426        node.named_child(0)
10427            .filter(|child| child.kind() == "function_declarator")
10428    }) else {
10429        return false;
10430    };
10431    let Some(name) = cpp_function_declarator_name_node(declarator) else {
10432        return false;
10433    };
10434    declarator.start_byte() == node.start_byte()
10435        && name.kind() == "identifier"
10436        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10437}
10438
10439/// Reparse a fragmented class-body interior while preserving its original byte
10440/// and line offsets. Unlike an included-range translation-unit parse, a padded
10441/// prefix keeps C++ preprocessor directives after an access label in the same
10442/// recovery shape tree-sitter produces for a complete class body.
10443fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
10444    let bytes = source.as_bytes();
10445    let prefix = bytes.get(..start)?;
10446    let interior = bytes.get(start..end)?;
10447    let mut padded = Vec::with_capacity(end);
10448    padded.extend(
10449        prefix
10450            .iter()
10451            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
10452    );
10453    padded.extend_from_slice(interior);
10454    let padded = String::from_utf8(padded).ok()?;
10455    let mut parser = Parser::new();
10456    parser
10457        .set_language(&tree_sitter_cpp::LANGUAGE.into())
10458        .ok()?;
10459    parser.parse(&padded, None)
10460}
10461
10462/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
10463/// reparsed interior is indexed only when every top-level named node is a
10464/// well-formed C++ item (or a comment) and at least one real item is present.
10465/// Expression/statement soup surfaces as a top-level `ERROR` or
10466/// `expression_statement`, neither of which is an item kind, so it is rejected.
10467///
10468/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
10469/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
10470/// swallowed by a preceding dangling sentinel) reparses to a real
10471/// `namespace_definition` whose body still holds a bogus `function_definition`,
10472/// so the subtree legitimately carries an error. Container items are admitted
10473/// even with an internal error; the inner bogus function is recovered recursively
10474/// when `visit_function_definition` walks it. Each recursion strips at least one
10475/// leading sentinel, so the region strictly shrinks and recovery terminates.
10476///
10477/// A top-level `function_definition` is the one place we stay strict: it is
10478/// admitted only when it is clean or is itself a sentinel candidate. A function
10479/// that has an error and is not a sentinel is a real callable with a broken body,
10480/// so we refuse the whole reparse and let the ordinary path handle it (preserving
10481/// its real return type rather than re-deriving an implicit one).
10482fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
10483    let mut cursor = root.walk();
10484    let mut saw_item = false;
10485    for child in root.named_children(&mut cursor) {
10486        match child.kind() {
10487            "comment" => {}
10488            "function_definition" => {
10489                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
10490                    return false;
10491                }
10492                saw_item = true;
10493            }
10494            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
10495            _ => return false,
10496        }
10497    }
10498    saw_item
10499}
10500
10501/// Robustness gate for a reparsed fragmented multiple-base export class body
10502/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
10503/// kinds a class body produces when reparsed at translation-unit scope: the
10504/// access-specifier label preceding the first member surfaces as a
10505/// `labeled_statement` wrapping that member, and members surface as
10506/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
10507/// Statement or expression soup surfaces as other top-level kinds and is rejected,
10508/// so only a genuinely member-shaped body is ever re-owned as members; anything
10509/// ambiguous falls back to indexing the class alone.
10510fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
10511    if node.kind() != "ERROR" {
10512        return false;
10513    }
10514    let mut stack = Vec::new();
10515    let mut saw_function_declarator = false;
10516    let mut cursor = node.walk();
10517    for child in node.named_children(&mut cursor) {
10518        stack.push(child);
10519    }
10520    while let Some(current) = stack.pop() {
10521        match current.kind() {
10522            // Tree-sitter may wrap adjacent copy-control declarations in a
10523            // nested ERROR. Keep descending only through ERROR wrappers; the
10524            // actual declaration payload must be a function_declarator.
10525            "ERROR" => {
10526                let mut cursor = current.walk();
10527                stack.extend(current.named_children(&mut cursor));
10528            }
10529            "function_declarator" => saw_function_declarator = true,
10530            _ => return false,
10531        }
10532    }
10533    saw_function_declarator
10534}
10535
10536fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
10537    if node.kind() != "ERROR" {
10538        return false;
10539    }
10540    let mut cursor = node.walk();
10541    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10542    let [explicit, constructor_error, destructor] = named.as_slice() else {
10543        return false;
10544    };
10545    let Some(constructor) = constructor_error.named_child(0) else {
10546        return false;
10547    };
10548    let Some(constructor_name) =
10549        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
10550    else {
10551        return false;
10552    };
10553    let Some(destructor_name) =
10554        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
10555    else {
10556        return false;
10557    };
10558    let Some(destroyed_type) = destructor_name.named_child(0) else {
10559        return false;
10560    };
10561    explicit.kind() == "explicit_function_specifier"
10562        && constructor_error.kind() == "ERROR"
10563        && constructor_error.named_child_count() == 1
10564        && constructor.kind() == "function_declarator"
10565        && constructor_name.kind() == "identifier"
10566        && destructor.kind() == "function_declarator"
10567        && destructor_name.kind() == "destructor_name"
10568        && destroyed_type.kind() == "identifier"
10569        && node_text(constructor_name, source) == node_text(destroyed_type, source)
10570}
10571
10572fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
10573    if node.kind() != "compound_statement" {
10574        return false;
10575    }
10576    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
10577        return false;
10578    };
10579    if prefix.kind() == "labeled_statement"
10580        && prefix.named_child(0).is_some_and(|label| {
10581            matches!(
10582                node_text(label, source).trim(),
10583                "public" | "private" | "protected"
10584            )
10585        })
10586    {
10587        return prefix.named_children(&mut prefix.walk()).any(|child| {
10588            child.kind() == "declaration"
10589                && child.has_error()
10590                && child
10591                    .named_children(&mut child.walk())
10592                    .any(cpp_reparsed_member_error_is_indexable)
10593        });
10594    }
10595    // A malformed constructor initializer can be split into a declaration
10596    // followed by its compound body when the class prefix already contains
10597    // realistic members. Keep this admission tied to that exact structured
10598    // declaration/error/body chain rather than accepting arbitrary blocks.
10599    prefix.kind() == "declaration"
10600        && prefix.has_error()
10601        && prefix
10602            .named_children(&mut prefix.walk())
10603            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10604}
10605
10606fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10607    if !cpp_reparsed_member_error_is_indexable(node) {
10608        return false;
10609    }
10610    let Some(preproc) = node.next_named_sibling() else {
10611        return false;
10612    };
10613    preproc.kind() == "preproc_if"
10614        && preproc.has_error()
10615        && preproc
10616            .named_children(&mut preproc.walk())
10617            .any(|child| child.kind() == "expression_statement" && child.has_error())
10618        && preproc
10619            .next_named_sibling()
10620            .is_some_and(|body| body.kind() == "compound_statement")
10621}
10622
10623/// Return a function body whose braces and ownership are explicit in the
10624/// reparsed class-member tree. An error below a real function envelope is
10625/// recoverable by the ordinary function visitor; a missing/deferred body is
10626/// not, because accepting it would let statement soup masquerade as a member.
10627fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
10628    if node.kind() != "function_definition" {
10629        return None;
10630    }
10631    let body = node.child_by_field_name("body")?;
10632    if body.kind() != "compound_statement" {
10633        return None;
10634    }
10635    let open = body.child(0)?;
10636    let close = body.child(body.child_count().checked_sub(1)?)?;
10637    if open.kind() != "{"
10638        || open.is_missing()
10639        || close.kind() != "}"
10640        || close.is_missing()
10641        || close.end_byte() != body.end_byte()
10642        || body.end_byte() != node.end_byte()
10643    {
10644        return None;
10645    }
10646    Some(body)
10647}
10648
10649fn cpp_reparsed_member_function_errors_are_in_body(
10650    node: Node<'_>,
10651    body: Node<'_>,
10652    source: &str,
10653) -> bool {
10654    let mut cursor = node.walk();
10655    node.children(&mut cursor).all(|child| {
10656        same_node(child, body)
10657            || cpp_reparsed_member_attribute_error(child, source)
10658            || cpp_reparsed_member_signature_identifier_errors(child)
10659            || (!child.has_error() && !child.is_error() && !child.is_missing())
10660    })
10661}
10662
10663/// A complete callable can still carry parser errors in its signature when a
10664/// project annotation is not part of the C++ grammar (`nonneg int`,
10665/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
10666/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
10667/// leaves inside the already-proven callable envelope; structured statements,
10668/// literals, missing tokens, and other malformed signature payload remain
10669/// rejected.
10670fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
10671    if !node.has_error() && !node.is_error() && !node.is_missing() {
10672        return false;
10673    }
10674    let mut stack = vec![node];
10675    let mut saw_error = false;
10676    while let Some(current) = stack.pop() {
10677        if current.is_missing() {
10678            return false;
10679        }
10680        if current.kind() == "ERROR" {
10681            saw_error = true;
10682            let mut cursor = current.walk();
10683            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
10684            if children
10685                .iter()
10686                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
10687            {
10688                return false;
10689            }
10690            stack.extend(children);
10691            continue;
10692        }
10693        let mut cursor = current.walk();
10694        stack.extend(current.children(&mut cursor));
10695    }
10696    saw_error
10697}
10698
10699fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
10700    node.kind() == "ERROR"
10701        && node.named_child_count() == 1
10702        && node.named_child(0).is_some_and(|attribute| {
10703            attribute.kind() == "identifier"
10704                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
10705        })
10706}
10707
10708/// A C++ attribute placed between a member's declarator and body can make
10709/// tree-sitter expose the callable as
10710/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
10711/// Keep this admission tied to that exact node geometry. In particular, an
10712/// arbitrary ERROR or identifier before a compound statement is not enough.
10713fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
10714    let Some(body) = cpp_reparsed_member_function_body(node) else {
10715        return false;
10716    };
10717    let mut cursor = node.walk();
10718    let named = node
10719        .named_children(&mut cursor)
10720        .filter(|child| child.kind() != "comment")
10721        .collect::<Vec<_>>();
10722    let [type_node, error, attribute, body_node] = named.as_slice() else {
10723        return false;
10724    };
10725    if !same_node(*body_node, body)
10726        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10727        || attribute.kind() != "identifier"
10728        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10729        || error.kind() != "ERROR"
10730        || error.named_child_count() != 1
10731    {
10732        return false;
10733    }
10734    error
10735        .named_child(0)
10736        .is_some_and(cpp_reparsed_attribute_callable_declarator)
10737}
10738
10739fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
10740    cpp_structured_type_path(node, source).is_some()
10741        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
10742}
10743
10744fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10745    let Some(body) = cpp_reparsed_member_function_body(node) else {
10746        return false;
10747    };
10748    let mut cursor = node.walk();
10749    let named = node
10750        .named_children(&mut cursor)
10751        .filter(|child| child.kind() != "comment")
10752        .collect::<Vec<_>>();
10753    let [friend, return_error, declarator, body_node] = named.as_slice() else {
10754        return false;
10755    };
10756    let Some(return_type) = return_error.named_child(0) else {
10757        return false;
10758    };
10759    same_node(*body_node, body)
10760        && friend.kind() == "type_identifier"
10761        && node_text(*friend, source) == "friend"
10762        && return_error.kind() == "ERROR"
10763        && return_error.named_child_count() == 1
10764        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10765        && extract_function_declarator(*declarator)
10766            .and_then(cpp_function_declarator_name_node)
10767            .is_some()
10768}
10769
10770fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10771    let Some(body) = cpp_reparsed_member_function_body(node) else {
10772        return false;
10773    };
10774    let mut cursor = node.walk();
10775    let named = node
10776        .named_children(&mut cursor)
10777        .filter(|child| child.kind() != "comment")
10778        .collect::<Vec<_>>();
10779    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
10780        return false;
10781    };
10782    let Some(return_type) = return_error.named_child(0) else {
10783        return false;
10784    };
10785    same_node(*body_node, body)
10786        && prefix
10787            .iter()
10788            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
10789        && attribute.kind() == "type_identifier"
10790        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10791        && return_error.kind() == "ERROR"
10792        && return_error.named_child_count() == 1
10793        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10794        && extract_function_declarator(*declarator)
10795            .and_then(cpp_function_declarator_name_node)
10796            .is_some()
10797}
10798
10799/// An included-range reparse that begins inside a malformed class can merge an
10800/// access label and following template member. Tree-sitter then emits the label
10801/// as the `template_type` name, the template parameter list as its arguments,
10802/// an ERROR-wrapped return type, the callable declarator, and its complete
10803/// body. Admit only that exact structured displacement.
10804fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
10805    let Some(body) = cpp_reparsed_member_function_body(node) else {
10806        return false;
10807    };
10808    let mut cursor = node.walk();
10809    let named = node
10810        .named_children(&mut cursor)
10811        .filter(|child| child.kind() != "comment")
10812        .collect::<Vec<_>>();
10813    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
10814        return false;
10815    };
10816    let Some(template_name) = template_type.child_by_field_name("name") else {
10817        return false;
10818    };
10819    let Some(arguments) = template_type.child_by_field_name("arguments") else {
10820        return false;
10821    };
10822    let Some(return_type) = return_error.named_child(0) else {
10823        return false;
10824    };
10825    let mut cursor = template_type.walk();
10826    let template_errors = template_type
10827        .named_children(&mut cursor)
10828        .filter(|child| child.kind() == "ERROR")
10829        .collect::<Vec<_>>();
10830    let [comment_error] = template_errors.as_slice() else {
10831        return false;
10832    };
10833    let mut cursor = comment_error.walk();
10834    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
10835    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
10836        return false;
10837    };
10838    same_node(*body_node, body)
10839        && template_type.kind() == "template_type"
10840        && template_name.kind() == "type_identifier"
10841        && matches!(
10842            node_text(template_name, source).trim(),
10843            "public" | "private" | "protected"
10844        )
10845        && arguments.kind() == "template_argument_list"
10846        && arguments.named_child_count() > 0
10847        && !arguments.has_error()
10848        && !colon.is_named()
10849        && colon.kind() == ":"
10850        && comments.iter().all(|child| child.kind() == "comment")
10851        && !template_keyword.is_named()
10852        && template_keyword.kind() == "template"
10853        && return_error.kind() == "ERROR"
10854        && return_error.named_child_count() == 1
10855        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
10856        && extract_function_declarator(*declarator)
10857            .and_then(cpp_function_declarator_name_node)
10858            .is_some()
10859}
10860
10861/// Return the constructor declaration tree-sitter can merge into an access
10862/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
10863/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
10864/// declaration's apparent type; the callable name must still exactly match the
10865/// recovered class, so unrelated labeled statements are never re-owned.
10866fn cpp_reparsed_preprocessor_constructor<'tree>(
10867    node: Node<'tree>,
10868    class_name: &str,
10869    source: &str,
10870) -> Option<Node<'tree>> {
10871    if node.kind() != "labeled_statement" {
10872        return None;
10873    }
10874    let mut cursor = node.walk();
10875    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10876    let [label, directive_error, declaration] = named.as_slice() else {
10877        return None;
10878    };
10879    if label.kind() != "statement_identifier"
10880        || !matches!(
10881            node_text(*label, source),
10882            "public" | "private" | "protected"
10883        )
10884        || directive_error.kind() != "ERROR"
10885        || directive_error.child_count() != 1
10886        || directive_error
10887            .child(0)
10888            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
10889        || declaration.kind() != "declaration"
10890        || declaration.named_child_count() != 2
10891    {
10892        return None;
10893    }
10894    let apparent_type = declaration.child_by_field_name("type")?;
10895    if apparent_type.kind() != "type_identifier"
10896        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
10897    {
10898        return None;
10899    }
10900    let declarator = declaration.child_by_field_name("declarator")?;
10901    let function = extract_function_declarator(declarator)?;
10902    let name = cpp_function_declarator_name_node(function)?;
10903    (node_text(name, source) == class_name).then_some(*declaration)
10904}
10905
10906fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
10907    if extract_function_declarator(node)
10908        .and_then(cpp_function_declarator_name_node)
10909        .is_some()
10910    {
10911        return true;
10912    }
10913    node.kind() == "init_declarator"
10914        && node
10915            .child_by_field_name("declarator")
10916            .is_some_and(|declarator| declarator.kind() == "identifier")
10917        && node
10918            .child_by_field_name("value")
10919            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
10920}
10921
10922/// Return true for the constrained/attribute form that tree-sitter splits into
10923/// an ERROR declaration, a preprocessor `requires` clause, and a following
10924/// compound statement. The three nodes must remain immediate named siblings;
10925/// this deliberately does not search source text or skip unrelated statements.
10926fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
10927    if node.kind() != "ERROR" || node.named_child_count() != 3 {
10928        return false;
10929    }
10930    let mut cursor = node.walk();
10931    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10932    let [type_node, function_declarator, attribute] = named.as_slice() else {
10933        return false;
10934    };
10935    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
10936        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
10937        || attribute.kind() != "identifier"
10938        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
10939    {
10940        return false;
10941    }
10942    let Some(preproc) =
10943        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
10944    else {
10945        return false;
10946    };
10947    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
10948        .filter(|sibling| sibling.kind() == "compound_statement")
10949    else {
10950        return false;
10951    };
10952    let Some(open) = body.child(0) else {
10953        return false;
10954    };
10955    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
10956        return false;
10957    };
10958    let Some(condition) = preproc.child_by_field_name("condition") else {
10959        return false;
10960    };
10961    let mut cursor = preproc.walk();
10962    let payload = preproc
10963        .named_children(&mut cursor)
10964        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
10965        .collect::<Vec<_>>();
10966    let [requires_statement] = payload.as_slice() else {
10967        return false;
10968    };
10969    let requires_clause = requires_statement.named_child(0);
10970
10971    open.kind() == "{"
10972        && !open.is_missing()
10973        && close.kind() == "}"
10974        && !close.is_missing()
10975        && close.end_byte() == body.end_byte()
10976        && requires_statement.kind() == "expression_statement"
10977        && requires_statement.named_child_count() == 1
10978        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
10979}
10980
10981fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10982    let mut sibling = node.next_named_sibling();
10983    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10984        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
10985    }
10986    sibling
10987}
10988
10989fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
10990    let mut sibling = node.prev_named_sibling();
10991    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
10992        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
10993    }
10994    sibling
10995}
10996
10997fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
10998    let Some(preproc) =
10999        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11000    else {
11001        return false;
11002    };
11003    let Some(error) =
11004        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
11005    else {
11006        return false;
11007    };
11008    cpp_reparsed_attribute_requires_error(error, source)
11009}
11010
11011fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
11012    node: Node<'tree>,
11013    source: &str,
11014) -> Option<Node<'tree>> {
11015    if node.kind() != "ERROR" {
11016        return None;
11017    }
11018    let mut cursor = node.walk();
11019    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11020    let [parameter, macro_name, message] = named.as_slice() else {
11021        return None;
11022    };
11023    let parameter_name = parameter.named_child(0)?;
11024    (parameter.kind() == "type_parameter_declaration"
11025        && parameter_name.kind() == "type_identifier"
11026        && macro_name.kind() == "type_identifier"
11027        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11028        && message.kind() == "string_literal")
11029        .then_some(parameter_name)
11030}
11031
11032/// Recognize the alternate constraint-macro prefix where tree-sitter retains
11033/// the complete qualified constraint as a fourth child instead of moving it
11034/// into the following function. Keep the gate tied to a two-type template
11035/// constraint that names the declared type parameter.
11036fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
11037    node: Node<'tree>,
11038    source: &str,
11039) -> Option<Node<'tree>> {
11040    if node.kind() != "ERROR" {
11041        return None;
11042    }
11043    let mut cursor = node.walk();
11044    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11045    let [parameter, macro_name, message, constraint] = named.as_slice() else {
11046        return None;
11047    };
11048    let parameter_name = parameter.named_child(0)?;
11049    let constraint_scope = constraint.child_by_field_name("scope")?;
11050    let constraint_template = constraint.child_by_field_name("name")?;
11051    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11052    let mut argument_cursor = constraint_arguments.walk();
11053    let constraint_types = constraint_arguments
11054        .named_children(&mut argument_cursor)
11055        .collect::<Vec<_>>();
11056    if parameter.kind() != "type_parameter_declaration"
11057        || parameter_name.kind() != "type_identifier"
11058        || macro_name.kind() != "type_identifier"
11059        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11060        || message.kind() != "string_literal"
11061        || constraint.kind() != "qualified_identifier"
11062        || constraint_scope.kind() != "namespace_identifier"
11063        || !matches!(
11064            constraint_template.kind(),
11065            "template_function" | "template_type"
11066        )
11067        || !matches!(constraint_types.as_slice(), [left, right]
11068            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11069        || constraint_arguments.has_error()
11070    {
11071        return None;
11072    }
11073    let parameter_text = node_text(parameter_name, source);
11074    let mut stack = constraint_types;
11075    while let Some(current) = stack.pop() {
11076        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
11077            return Some(parameter_name);
11078        }
11079        let mut cursor = current.walk();
11080        stack.extend(current.named_children(&mut cursor));
11081    }
11082    None
11083}
11084
11085fn cpp_reparsed_template_macro_companion_is_indexable(
11086    node: Node<'_>,
11087    parameter_name: Node<'_>,
11088    source: &str,
11089) -> bool {
11090    let Some(body) = cpp_reparsed_member_function_body(node) else {
11091        return false;
11092    };
11093    let mut cursor = node.walk();
11094    let named = node
11095        .named_children(&mut cursor)
11096        .filter(|child| child.kind() != "comment")
11097        .collect::<Vec<_>>();
11098    let [
11099        constraint,
11100        close_error,
11101        storage,
11102        return_error,
11103        declarator,
11104        body_node,
11105    ] = named.as_slice()
11106    else {
11107        return false;
11108    };
11109    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
11110        return false;
11111    };
11112    let Some(constraint_template) = constraint.child_by_field_name("name") else {
11113        return false;
11114    };
11115    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
11116        return false;
11117    };
11118    let Some(return_type) = return_error.named_child(0) else {
11119        return false;
11120    };
11121    let mut cursor = constraint_arguments.walk();
11122    let constraint_types = constraint_arguments
11123        .named_children(&mut cursor)
11124        .collect::<Vec<_>>();
11125    same_node(*body_node, body)
11126        && constraint.kind() == "qualified_identifier"
11127        && constraint_scope.kind() == "namespace_identifier"
11128        && constraint_template.kind() == "template_type"
11129        && matches!(constraint_types.as_slice(), [left, right]
11130            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11131        && !constraint_arguments.has_error()
11132        && close_error.kind() == "ERROR"
11133        && close_error.named_child_count() == 0
11134        && storage.kind() == "storage_class_specifier"
11135        && return_error.kind() == "ERROR"
11136        && return_error.named_child_count() == 1
11137        && return_type.kind() == "identifier"
11138        && node_text(return_type, source) == node_text(parameter_name, source)
11139        && extract_function_declarator(*declarator)
11140            .and_then(cpp_function_declarator_name_node)
11141            .is_some()
11142}
11143
11144fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
11145    node: Node<'tree>,
11146    parameter_name: Node<'_>,
11147    source: &str,
11148) -> Option<Node<'tree>> {
11149    let body = cpp_reparsed_member_function_body(node)?;
11150    let constraint = node.child_by_field_name("type")?;
11151    let constraint_template = constraint.child_by_field_name("name")?;
11152    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11153    let mut argument_cursor = constraint_arguments.walk();
11154    let constraint_types = constraint_arguments
11155        .named_children(&mut argument_cursor)
11156        .collect::<Vec<_>>();
11157    if constraint.kind() != "qualified_identifier"
11158        || constraint_template.kind() != "template_type"
11159        || !matches!(constraint_types.as_slice(), [left, right]
11160            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11161        || constraint_arguments.has_error()
11162        || node
11163            .child_by_field_name("body")
11164            .is_none_or(|candidate| !same_node(candidate, body))
11165    {
11166        return None;
11167    }
11168
11169    let mut cursor = node.walk();
11170    let recovery_errors = node
11171        .named_children(&mut cursor)
11172        .filter(|child| child.kind() == "ERROR")
11173        .collect::<Vec<_>>();
11174    if !recovery_errors
11175        .iter()
11176        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
11177        || !recovery_errors.iter().all(|error| {
11178            error.named_child_count() == 0
11179                || cpp_reparsed_constraint_macro_error(*error, source)
11180                || (error.named_child_count() == 1
11181                    && error
11182                        .named_child(0)
11183                        .is_some_and(|child| child.kind() == "function_declarator"))
11184        })
11185    {
11186        return None;
11187    }
11188
11189    let parameter_text = node_text(parameter_name, source);
11190    let mut declarators = node
11191        .child_by_field_name("declarator")
11192        .and_then(extract_function_declarator)
11193        .into_iter()
11194        .collect::<Vec<_>>();
11195    for error in recovery_errors {
11196        let mut stack = vec![error];
11197        while let Some(current) = stack.pop() {
11198            if current.kind() == "function_declarator" {
11199                declarators.push(current);
11200            }
11201            let mut cursor = current.walk();
11202            stack.extend(current.named_children(&mut cursor));
11203        }
11204    }
11205    declarators.into_iter().find(|declarator| {
11206        cpp_function_declarator_name_node(*declarator)
11207            .is_some_and(|name| name.kind() == "identifier")
11208            && declarator
11209                .child_by_field_name("parameters")
11210                .is_some_and(|parameters| {
11211                    parameters
11212                        .named_children(&mut parameters.walk())
11213                        .filter_map(|parameter| parameter.child_by_field_name("type"))
11214                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
11215                })
11216    })
11217}
11218
11219fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
11220    node: Node<'_>,
11221    parameter_name: Node<'_>,
11222    source: &str,
11223) -> bool {
11224    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
11225}
11226
11227fn cpp_reparsed_template_macro_function_companion_is_indexable(
11228    node: Node<'_>,
11229    parameter_name: Node<'_>,
11230    source: &str,
11231) -> bool {
11232    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
11233        return false;
11234    }
11235    let Some(return_type) = node.child_by_field_name("type") else {
11236        return false;
11237    };
11238    let Some(function_declarator) = node
11239        .child_by_field_name("declarator")
11240        .and_then(extract_function_declarator)
11241    else {
11242        return false;
11243    };
11244    if cpp_function_declarator_name_node(function_declarator).is_none()
11245        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
11246    {
11247        return false;
11248    }
11249    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
11250        return false;
11251    };
11252    let parameter_text = node_text(parameter_name, source);
11253    parameters
11254        .named_children(&mut parameters.walk())
11255        .any(|parameter| {
11256            parameter
11257                .child_by_field_name("type")
11258                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
11259        })
11260}
11261
11262fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
11263    if node.kind() != "ERROR" {
11264        return false;
11265    }
11266    let mut stack = vec![node];
11267    while let Some(current) = stack.pop() {
11268        let macro_shape = match current.kind() {
11269            "call_expression" => current
11270                .child_by_field_name("function")
11271                .zip(current.child_by_field_name("arguments")),
11272            "init_declarator" => current
11273                .child_by_field_name("declarator")
11274                .zip(current.child_by_field_name("value")),
11275            _ => None,
11276        };
11277        if let Some((name, arguments)) = macro_shape
11278            && name.kind() == "identifier"
11279            && arguments.kind() == "argument_list"
11280            && arguments.named_child_count() >= 2
11281            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11282        {
11283            return true;
11284        }
11285        let mut cursor = current.walk();
11286        stack.extend(current.named_children(&mut cursor));
11287    }
11288    false
11289}
11290
11291fn cpp_recovered_template_macro_constructor<'tree>(
11292    node: Node<'tree>,
11293    source: &str,
11294) -> Option<(Node<'tree>, Node<'tree>)> {
11295    let mut prefix = node.prev_named_sibling()?;
11296    while prefix.kind() == "comment" {
11297        prefix = prefix.prev_named_sibling()?;
11298    }
11299    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
11300    let parameter = parameter_name
11301        .parent()
11302        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
11303    let declarator =
11304        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
11305    Some((declarator, parameter))
11306}
11307
11308fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
11309    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
11310        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11311            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
11312                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
11313                    function,
11314                    parameter_name,
11315                    source,
11316                )
11317        });
11318    }
11319    let Some(parameter_name) =
11320        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
11321    else {
11322        return false;
11323    };
11324    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11325        cpp_reparsed_template_macro_function_companion_is_indexable(
11326            function,
11327            parameter_name,
11328            source,
11329        )
11330    })
11331}
11332
11333fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11334    let function_name = node
11335        .child_by_field_name("declarator")
11336        .and_then(extract_function_declarator)
11337        .and_then(cpp_function_declarator_name_node);
11338    if let Some(body) = cpp_reparsed_member_function_body(node)
11339        && function_name.is_some()
11340        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
11341    {
11342        return true;
11343    }
11344    cpp_reparsed_attribute_member_function(node, source)
11345        || cpp_reparsed_friend_function_is_indexable(node, source)
11346        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
11347        || cpp_reparsed_access_template_function_is_indexable(node, source)
11348        || cpp_recovered_template_macro_constructor(node, source).is_some()
11349}
11350
11351/// Recognize the three top-level nodes produced when an unknown attribute
11352/// macro separates an inline member's declarator from its body in a reparsed
11353/// class interior: an errorful declaration with a missing semicolon, the macro
11354/// call expression, and the complete compound body. Their adjacency and exact
11355/// structured shapes prove one recoverable member envelope; arbitrary calls or
11356/// blocks do not pass this gate.
11357fn cpp_reparsed_macro_attribute_member_sequence(
11358    children: &[Node<'_>],
11359    index: usize,
11360    source: &str,
11361) -> bool {
11362    let Some(prefix) = children.get(index).copied() else {
11363        return false;
11364    };
11365    let declaration = if prefix.kind() == "labeled_statement" {
11366        prefix
11367            .named_child(prefix.named_child_count().saturating_sub(1))
11368            .filter(|child| child.kind() == "declaration")
11369    } else {
11370        (prefix.kind() == "declaration").then_some(prefix)
11371    };
11372    let Some(declaration) = declaration else {
11373        return false;
11374    };
11375    if !declaration.has_error()
11376        || declaration
11377            .child_by_field_name("declarator")
11378            .and_then(extract_function_declarator)
11379            .and_then(cpp_function_declarator_name_node)
11380            .is_none()
11381    {
11382        return false;
11383    }
11384    let Some(attribute_statement) = children.get(index + 1).copied() else {
11385        return false;
11386    };
11387    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
11388        .then(|| attribute_statement.named_child(0))
11389        .flatten()
11390        .filter(|child| child.kind() == "call_expression")
11391    else {
11392        return false;
11393    };
11394    let Some(attribute_name) = attribute_call
11395        .child_by_field_name("function")
11396        .filter(|function| function.kind() == "identifier")
11397        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
11398    else {
11399        return false;
11400    };
11401    if !cpp_export_macro_token(&attribute_name) {
11402        return false;
11403    }
11404    let Some(body) = children.get(index + 2).copied() else {
11405        return false;
11406    };
11407    body.kind() == "compound_statement"
11408        && body.child(0).is_some_and(|open| open.kind() == "{")
11409        && body
11410            .child(body.child_count().saturating_sub(1))
11411            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
11412        && declaration.end_byte() <= attribute_statement.start_byte()
11413        && attribute_statement.end_byte() <= body.start_byte()
11414}
11415
11416fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
11417    let mut cursor = root.walk();
11418    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
11419    let mut saw_member = false;
11420    let mut index = 0;
11421    while index < children.len() {
11422        let child = children[index];
11423        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
11424            saw_member = true;
11425            index += 3;
11426            continue;
11427        }
11428        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
11429            let Some(tree) = cpp_reparse_fragmented_class_body(
11430                source,
11431                fragmented.reparse_start,
11432                fragmented.reparse_end,
11433            ) else {
11434                return false;
11435            };
11436            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11437                return false;
11438            }
11439            saw_member = true;
11440            index += 1;
11441            while index < children.len()
11442                && children[index].end_byte() <= fragmented.class_range.end_byte
11443            {
11444                index += 1;
11445            }
11446            continue;
11447        }
11448        match child.kind() {
11449            "comment" => {}
11450            "labeled_statement" => saw_member = true,
11451            "function_definition" => {
11452                if child.has_error()
11453                    && !cpp_reparsed_member_function_is_indexable(child, source)
11454                    && cpp_sentinel_macro_region(child, source).is_none()
11455                {
11456                    return false;
11457                }
11458                saw_member = true;
11459            }
11460            "ERROR"
11461                if (cpp_reparsed_member_error_is_indexable(child)
11462                    || cpp_reparsed_adjacent_copy_control_error(child, source))
11463                    && (child
11464                        .next_named_sibling()
11465                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
11466                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
11467            {
11468                saw_member = true;
11469            }
11470            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
11471                saw_member = true;
11472            }
11473            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
11474                saw_member = true;
11475            }
11476            "expression_statement"
11477                if cpp_is_stray_semicolon(child, source)
11478                    && child.prev_named_sibling().is_some_and(|error| {
11479                        cpp_reparsed_member_error_is_indexable(error)
11480                            || cpp_reparsed_adjacent_copy_control_error(error, source)
11481                    }) =>
11482            {
11483                saw_member = true;
11484            }
11485            "compound_statement"
11486                if cpp_reparsed_constructor_body_is_indexable(child, source)
11487                    || cpp_reparsed_attribute_requires_body(child, source) =>
11488            {
11489                saw_member = true;
11490            }
11491            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
11492            _ => return false,
11493        }
11494        index += 1;
11495    }
11496    saw_member
11497}
11498
11499/// Detect the malformed constructor shape that tree-sitter exposes as an
11500/// access-label statement followed by initializer-looking declarations. The
11501/// declarations are not class members: visiting their `location(loc)` and
11502/// `string(s)` function declarators would publish synthetic functions. The
11503/// export-class fallback keeps the original sibling nodes and therefore avoids
11504/// this parser artifact. The returned range identifies the real constructor
11505/// header, which can be reparsed independently as a structured declarator.
11506fn cpp_reparsed_synthetic_initializer_constructor_range(
11507    root: Node<'_>,
11508    class_name: &str,
11509    source: &str,
11510    constructor_end: usize,
11511) -> Option<std::ops::Range<usize>> {
11512    let mut stack = {
11513        let mut cursor = root.walk();
11514        root.named_children(&mut cursor).collect::<Vec<_>>()
11515    };
11516    while let Some(current) = stack.pop() {
11517        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
11518            current,
11519            class_name,
11520            source,
11521            constructor_end,
11522        ) {
11523            return Some(range);
11524        }
11525        if current.kind() == "ERROR" {
11526            let mut cursor = current.walk();
11527            stack.extend(current.named_children(&mut cursor));
11528        }
11529    }
11530    None
11531}
11532
11533fn cpp_reparsed_synthetic_initializer_constructor(
11534    node: Node<'_>,
11535    class_name: &str,
11536    source: &str,
11537    constructor_end: usize,
11538) -> Option<std::ops::Range<usize>> {
11539    if node.kind() != "labeled_statement" {
11540        return None;
11541    }
11542    let mut cursor = node.walk();
11543    let named = node
11544        .named_children(&mut cursor)
11545        .filter(|child| child.kind() != "comment")
11546        .collect::<Vec<_>>();
11547    let label = named.first()?;
11548    if label.kind() != "statement_identifier"
11549        || !matches!(
11550            node_text(*label, source).trim(),
11551            "public" | "private" | "protected"
11552        )
11553    {
11554        return None;
11555    }
11556    let call_error_index = named.iter().position(|child| {
11557        if child.kind() != "ERROR" {
11558            return false;
11559        }
11560        let mut stack = vec![*child];
11561        while let Some(current) = stack.pop() {
11562            if current.kind() == "call_expression"
11563                && current
11564                    .child_by_field_name("function")
11565                    .is_some_and(|function| {
11566                        function.kind() == "identifier"
11567                            && node_text(function, source).trim() == class_name
11568                    })
11569            {
11570                return true;
11571            }
11572            let mut cursor = current.walk();
11573            stack.extend(current.named_children(&mut cursor));
11574        }
11575        false
11576    })?;
11577    let constructor_call = {
11578        let mut stack = vec![named[call_error_index]];
11579        let mut found = None;
11580        while let Some(current) = stack.pop() {
11581            if current.kind() == "call_expression"
11582                && current
11583                    .child_by_field_name("function")
11584                    .is_some_and(|function| {
11585                        function.kind() == "identifier"
11586                            && node_text(function, source).trim() == class_name
11587                    })
11588            {
11589                found = Some(current);
11590                break;
11591            }
11592            let mut cursor = current.walk();
11593            stack.extend(current.named_children(&mut cursor));
11594        }
11595        found
11596    };
11597    let constructor_call = constructor_call?;
11598    named.iter().skip(call_error_index + 1).find(|child| {
11599        child.kind() == "declaration" && child.has_error() && {
11600            let mut cursor = child.walk();
11601            child.named_children(&mut cursor).any(|declarator| {
11602                declarator.kind() == "init_declarator"
11603                    && declarator
11604                        .child_by_field_name("declarator")
11605                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
11606                    && declarator
11607                        .child_by_field_name("value")
11608                        .is_some_and(|value| value.kind() == "initializer_list")
11609            })
11610        }
11611    })?;
11612    Some(constructor_call.start_byte()..constructor_end)
11613}
11614
11615fn cpp_reparsed_exact_constructor_declarator<'tree>(
11616    root: Node<'tree>,
11617    start: usize,
11618    class_name: &str,
11619    source: &str,
11620) -> Option<Node<'tree>> {
11621    let mut candidate = None;
11622    let mut stack = vec![root];
11623    while let Some(current) = stack.pop() {
11624        if current.kind() == "function_declarator"
11625            && current.start_byte() == start
11626            && cpp_function_declarator_name_node(current)
11627                .is_some_and(|name| node_text(name, source).trim() == class_name)
11628        {
11629            if candidate.is_some() {
11630                return None;
11631            }
11632            candidate = Some(current);
11633            continue;
11634        }
11635        let mut cursor = current.walk();
11636        stack.extend(current.named_children(&mut cursor));
11637    }
11638    candidate
11639}
11640
11641fn cpp_is_indexable_item_kind(kind: &str) -> bool {
11642    matches!(
11643        kind,
11644        "namespace_definition"
11645            | "class_specifier"
11646            | "struct_specifier"
11647            | "union_specifier"
11648            | "enum_specifier"
11649            | "function_definition"
11650            | "template_declaration"
11651            | "declaration"
11652            | "field_declaration"
11653            | "alias_declaration"
11654            | "static_assert_declaration"
11655            | "type_definition"
11656            | "using_declaration"
11657            | "linkage_specification"
11658            | "preproc_def"
11659            | "preproc_function_def"
11660            | "preproc_include"
11661            | "preproc_if"
11662            | "preproc_ifdef"
11663            | "preproc_call"
11664    )
11665}
11666
11667#[cfg(test)]
11668mod tests {
11669    use super::*;
11670    use crate::adapter::parse_cpp_file;
11671    use brokk_bifrost_core::analyzer::parsed_file::{
11672        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
11673    };
11674    use std::fmt::Write;
11675
11676    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
11677        let mut parser = tree_sitter::Parser::new();
11678        parser
11679            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11680            .unwrap();
11681        let tree = parser.parse(source, None).unwrap();
11682        let file = ProjectFile::new(std::env::temp_dir(), name);
11683        parse_cpp_file(&file, source, &tree)
11684    }
11685
11686    #[test]
11687    fn identifies_export_macro_class_base_displaced_into_declarator() {
11688        let source = r#"#define PROJECT_API_
11689namespace project {
11690namespace internal {
11691template <typename T>
11692class Base {};
11693}
11694template <typename T>
11695class Wrapper;
11696template <>
11697class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
11698}
11699"#;
11700        let mut parser = tree_sitter::Parser::new();
11701        parser
11702            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11703            .unwrap();
11704        let tree = parser.parse(source, None).unwrap();
11705        let start = source.find("internal::Base<int>").expect("base");
11706        let mut base = tree
11707            .root_node()
11708            .descendant_for_byte_range(start, start + 8)
11709            .expect("base syntax");
11710        while base.kind() != "qualified_identifier" {
11711            base = base.parent().expect("qualified base ancestor");
11712        }
11713        assert!(
11714            is_recovered_exported_class_base_type_node(base, source),
11715            "{}",
11716            tree.root_node().to_sexp()
11717        );
11718    }
11719
11720    #[test]
11721    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
11722        let source = r#"namespace control {
11723template <typename T>
11724class AnySpan;
11725template <typename T>
11726class ABSL_ATTRIBUTE_VIEW AnySpan {
11727 public:
11728  int begin() const;
11729};
11730}
11731
11732namespace absl {
11733ABSL_NAMESPACE_BEGIN
11734template <typename T>
11735class ABSL_ATTRIBUTE_VIEW Span {
11736 public:
11737  int begin() const;
11738  int back() const;
11739};
11740
11741int begin();
11742int back();
11743}
11744"#;
11745        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
11746        let declarations = parsed.declarations();
11747        assert!(
11748            declarations
11749                .iter()
11750                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
11751        );
11752        for method in ["begin", "back"] {
11753            assert!(declarations.iter().any(|unit| {
11754                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
11755            }));
11756            assert!(
11757                declarations.iter().any(|unit| {
11758                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
11759                })
11760            );
11761        }
11762        assert!(
11763            declarations
11764                .iter()
11765                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
11766        );
11767        assert!(
11768            declarations
11769                .iter()
11770                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
11771        );
11772        assert!(
11773            declarations
11774                .iter()
11775                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
11776        );
11777    }
11778
11779    #[test]
11780    fn explicit_global_member_definition_has_canonical_package_boundary() {
11781        let source = r#"
11782namespace arangodb::aql {
11783class ExecutionPlan {
11784 public:
11785  template<class... Args> Node* createNode(Args&&... args);
11786};
11787}
11788
11789template<class... Args>
11790Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
11791"#;
11792        let parsed = parse_cpp_declarations(source, "global-member.cpp");
11793
11794        assert!(parsed.declarations().iter().any(|unit| {
11795            unit.is_function()
11796                && unit.package_name() == "arangodb::aql"
11797                && unit.short_name() == "ExecutionPlan.createNode"
11798                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
11799        }));
11800    }
11801
11802    #[test]
11803    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
11804        let source = r#"
11805#ifndef TINYXML2_INCLUDED
11806#define TINYXML2_INCLUDED
11807namespace tinyxml2 {
11808class TINYXML2_LIB XMLUtil {
11809 public:
11810  static const char* SkipWhiteSpace(const char* p) {
11811    while (*p) {
11812      if (*p == ' ') {
11813        ++p;
11814      }
11815    }
11816    return p;
11817  }
11818  static bool StringEqual(const char* p, const char* q) {
11819    return p == q;
11820  }
11821  class TINYXML2_LIB Helper {
11822   public:
11823    void Touch();
11824  };
11825  static void ToStr(int value, char* buffer);
11826 private:
11827  static const char* writeBoolTrue;
11828};
11829
11830class TINYXML2_LIB XMLNode {
11831 public:
11832  virtual XMLNode* ShallowClone() const = 0;
11833  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
11834};
11835}
11836#endif
11837"#;
11838        let mut parser = tree_sitter::Parser::new();
11839        parser
11840            .set_language(&tree_sitter_cpp::LANGUAGE.into())
11841            .unwrap();
11842        let tree = parser.parse(source, None).unwrap();
11843        let mut boundary_found = false;
11844        walk_named_tree_preorder(tree.root_node(), true, |node| {
11845            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
11846                && name == "XMLUtil"
11847            {
11848                boundary_found = fragmented_export_sibling_class_boundary(node, source)
11849                    .and_then(|boundary| {
11850                        recover_exported_class_function_definition(boundary, source)
11851                    })
11852                    .is_some_and(|(_, name, _)| name == "XMLNode");
11853            }
11854            WalkControl::Continue
11855        });
11856        assert!(
11857            boundary_found,
11858            "fixture must exercise the recovered sibling boundary"
11859        );
11860
11861        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
11862        assert!(
11863            parsed
11864                .declarations()
11865                .iter()
11866                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
11867            "{:#?}",
11868            parsed.declarations()
11869        );
11870        assert!(
11871            parsed
11872                .declarations()
11873                .iter()
11874                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
11875            "{:#?}",
11876            parsed.declarations()
11877        );
11878        assert!(parsed.declarations().iter().any(|unit| {
11879            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
11880        }));
11881        assert!(
11882            parsed
11883                .declarations()
11884                .iter()
11885                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
11886        );
11887        assert!(
11888            parsed
11889                .declarations()
11890                .iter()
11891                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
11892        );
11893    }
11894
11895    #[test]
11896    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
11897        // Clang's diagnostic suite intentionally contains this ill-formed
11898        // spelling. The analyzer must retain the parser's explicit-global AST
11899        // boundary instead of constructing `cwg311::::cwg311::X`.
11900        let parsed = parse_cpp_declarations(
11901            r#"
11902namespace cwg311 {
11903namespace X { namespace Y {} }
11904namespace ::cwg311::X {}
11905}
11906"#,
11907            "explicit-global-namespace.cpp",
11908        );
11909
11910        assert!(parsed.declarations().iter().any(|unit| {
11911            unit.kind() == CodeUnitType::Module
11912                && unit.short_name() == "cwg311::X"
11913                && unit.fq_name() == "cwg311::X"
11914        }));
11915        assert!(
11916            parsed
11917                .declarations()
11918                .iter()
11919                .all(|unit| !unit.short_name().contains("::::")),
11920            "recovered namespace names must not retain empty scope components: {:#?}",
11921            parsed.declarations()
11922        );
11923    }
11924
11925    #[test]
11926    fn repeated_scope_separator_does_not_create_empty_function_owner() {
11927        let scope = ScopeInfo {
11928            package_name: "X".to_string(),
11929            module: None,
11930            class_unit: None,
11931            template_signature: None,
11932            template_metadata: None,
11933            declarations_are_fields: false,
11934            recovered_specialization_member_scope: false,
11935            visible_using_namespaces: Vec::new(),
11936        };
11937
11938        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
11939
11940        assert!(owner.is_none());
11941        assert_eq!(name, "doit");
11942        assert_eq!(package, "X");
11943    }
11944
11945    #[test]
11946    fn trailing_decltype_expression_is_not_a_function_declarator() {
11947        let source = r#"
11948namespace boost { namespace detail {
11949#if ! defined(BOOST_NO_SFINAE_EXPR) && \
11950    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
11951    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
11952#define BOOST_THREAD_PROVIDES_INVOKE
11953#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
11954template <class Fp, class A0, class ...Args>
11955inline auto
11956invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
11957       BOOST_THREAD_RV_REF(Args) ...args)
11958    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
11959{
11960    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
11961}
11962#endif
11963#endif
11964}}
11965"#;
11966        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
11967
11968        assert!(
11969            parsed
11970                .declarations()
11971                .iter()
11972                .all(|unit| unit.short_name() != ".*f")
11973        );
11974    }
11975
11976    fn find_class_named<'tree>(
11977        root: Node<'tree>,
11978        source: &str,
11979        expected_name: &str,
11980    ) -> Option<Node<'tree>> {
11981        let mut stack = vec![root];
11982        while let Some(node) = stack.pop() {
11983            if node.kind() == "class_specifier"
11984                && node
11985                    .child_by_field_name("name")
11986                    .is_some_and(|name| node_text(name, source) == expected_name)
11987            {
11988                return Some(node);
11989            }
11990            let mut cursor = node.walk();
11991            stack.extend(node.named_children(&mut cursor));
11992        }
11993        None
11994    }
11995
11996    #[test]
11997    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
11998        let source = r#"EXPORT void definition(struct Value value) {}
11999EXPORT void prototype(struct Value value);
12000"#;
12001        let mut parser = tree_sitter::Parser::new();
12002        parser
12003            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12004            .unwrap();
12005        let tree = parser.parse(source, None).unwrap();
12006        let root = tree.root_node();
12007        let mut cursor = root.walk();
12008        let callables = root
12009            .named_children(&mut cursor)
12010            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
12011            .collect::<Vec<_>>();
12012
12013        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
12014        for callable in callables {
12015            assert!(callable.has_error(), "fixture must exercise error recovery");
12016            assert!(
12017                cpp_sentinel_macro_parts(callable, source).is_none(),
12018                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
12019            );
12020        }
12021    }
12022
12023    #[test]
12024    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
12025        let source = r#"namespace absl {
12026ABSL_NAMESPACE_BEGIN
12027// Generate a floating-point variate conforming to a Beta distribution:
12028template <typename RealType = double>
12029class beta_distribution {
12030 public:
12031  using result_type = RealType;
12032
12033
12034  beta_distribution() : beta_distribution(1) {}
12035
12036  explicit beta_distribution(result_type alpha, result_type beta = 1)
12037      : param_(alpha, beta) {}
12038
12039  explicit beta_distribution(const param_type& p) : param_(p) {}
12040
12041  void reset() {}
12042
12043  // Generating functions
12044  template <typename URBG>
12045  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
12046    return (*this)(g, param_);
12047  }
12048
12049};
12050ABSL_NAMESPACE_END
12051}  // namespace absl
12052"#;
12053        let mut parser = tree_sitter::Parser::new();
12054        parser
12055            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12056            .unwrap();
12057        let tree = parser.parse(source, None).unwrap();
12058        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12059        let body = namespace
12060            .child_by_field_name("body")
12061            .expect("fixture namespace body");
12062        let sentinel = body.named_child(0).expect("sentinel envelope");
12063        let callable = sentinel
12064            .child_by_field_name("declarator")
12065            .and_then(extract_function_declarator)
12066            .and_then(cpp_function_declarator_name_node)
12067            .expect("preserved callable name");
12068
12069        assert_eq!(sentinel.kind(), "function_definition");
12070        assert_eq!(callable.kind(), "operator_name");
12071        assert!(
12072            cpp_sentinel_macro_parts(sentinel, source).is_some(),
12073            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
12074        );
12075    }
12076
12077    #[test]
12078    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
12079        let source = r#"namespace absl {
12080ABSL_NAMESPACE_BEGIN
12081// absl::discrete_distribution
12082//
12083// A discrete distribution produces random integers i, where 0 <= i < n
12084template <typename IntType = int>
12085class discrete_distribution {
12086 public:
12087  using result_type = IntType;
12088  class param_type {
12089   public:
12090    param_type() { init(); }
12091    template <typename InputIterator>
12092    explicit param_type(InputIterator begin, InputIterator end)
12093        : p_(begin, end) {
12094      init();
12095    }
12096  };
12097  discrete_distribution() : param_() {}
12098  explicit discrete_distribution(const param_type& p) : param_(p) {}
12099};
12100ABSL_NAMESPACE_END
12101}  // namespace absl
12102"#;
12103        let mut parser = tree_sitter::Parser::new();
12104        parser
12105            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12106            .unwrap();
12107        let tree = parser.parse(source, None).unwrap();
12108        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12109        let body = namespace
12110            .child_by_field_name("body")
12111            .expect("fixture namespace body");
12112        let sentinel = body.named_child(0).expect("sentinel envelope");
12113        let callable = sentinel
12114            .child_by_field_name("declarator")
12115            .and_then(extract_function_declarator)
12116            .and_then(cpp_function_declarator_name_node)
12117            .expect("preserved callable name");
12118
12119        assert_eq!(sentinel.kind(), "function_definition");
12120        assert_eq!(callable.kind(), "identifier");
12121        assert!(
12122            cpp_sentinel_macro_parts(sentinel, source).is_some(),
12123            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
12124        );
12125    }
12126
12127    #[test]
12128    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
12129        let source = r#"
12130#define CPPCHECKLIB
12131class Library {
12132    struct Container {
12133        CPPCHECKLIB static std::string toString(Yield yield);
12134        CPPCHECKLIB static std::string toString(Action action);
12135    };
12136};
12137"#;
12138        let mut parser = tree_sitter::Parser::new();
12139        parser
12140            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12141            .unwrap();
12142        let tree = parser.parse(source, None).unwrap();
12143        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
12144        let parsed = parse_cpp_file(&file, source, &tree);
12145        assert!(
12146            parsed
12147                .declarations()
12148                .iter()
12149                .all(|unit| unit.fq_name() != "Library$Container.std"),
12150            "the qualified return-type namespace must not become a field: {:#?}",
12151            parsed.declarations()
12152        );
12153        for expected in ["(Yield)", "(Action)"] {
12154            assert!(
12155                parsed.declarations().iter().any(|unit| {
12156                    unit.is_function()
12157                        && unit.fq_name() == "Library$Container.toString"
12158                        && unit.signature() == Some(expected)
12159                }),
12160                "recovered toString overload {expected} is missing: {:#?}",
12161                parsed.declarations()
12162            );
12163        }
12164    }
12165
12166    #[test]
12167    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
12168        let source = r#"
12169#define SIMPLECPP_LIB
12170namespace simplecpp {
12171using TokenString = std::string;
12172struct Location { int line{}; };
12173class SIMPLECPP_LIB Token {
12174  TokenString prefix;
12175  void prefix_method() {}
12176 public:
12177  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12178      whitespaceahead(wsahead), location(loc), string(s)
12179      // The comment must not hide the constructor body from recovery.
12180      {
12181      flags();
12182  }
12183  TokenString string;
12184  bool whitespaceahead;
12185  Location location;
12186  Token *previous{};
12187 private:
12188  void flags() {
12189      whitespaceahead = true;
12190  }
12191};
12192}
12193"#;
12194        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
12195
12196        let location_fields = parsed
12197            .declarations()
12198            .iter()
12199            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
12200            .collect::<Vec<_>>();
12201        assert_eq!(
12202            location_fields.len(),
12203            1,
12204            "location should have one class-owned declaration: {:#?}",
12205            parsed.declarations()
12206        );
12207        assert!(
12208            location_fields[0].is_field(),
12209            "location has wrong kind: {:#?}",
12210            parsed.declarations()
12211        );
12212        assert!(
12213            parsed.declarations().iter().all(|unit| {
12214                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
12215            })
12216        );
12217        assert!(
12218            parsed.declarations().iter().all(|unit| {
12219                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12220            })
12221        );
12222        assert!(
12223            parsed
12224                .declarations()
12225                .iter()
12226                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
12227        );
12228        assert!(
12229            parsed
12230                .declarations()
12231                .iter()
12232                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
12233            "the recovered class must retain its constructor: {:#?}",
12234            parsed.declarations()
12235        );
12236        assert!(
12237            parsed
12238                .declarations()
12239                .iter()
12240                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
12241        );
12242        assert!(parsed.declarations().iter().any(|unit| {
12243            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
12244        }));
12245        let constructor = parsed
12246            .declarations()
12247            .iter()
12248            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
12249            .expect("recovered constructor");
12250        let constructor_start = source.find("Token(const").expect("constructor start");
12251        let constructor_end = source
12252            .get(
12253                ..source
12254                    .find("  TokenString string;")
12255                    .expect("constructor end"),
12256            )
12257            .expect("constructor slice")
12258            .trim_end()
12259            .len();
12260        assert!(
12261            parsed
12262                .navigation_ranges
12263                .get(constructor)
12264                .is_some_and(|ranges| {
12265                    ranges.iter().any(|range| {
12266                        range.start_byte == constructor_start && range.end_byte == constructor_end
12267                    })
12268                }),
12269            "constructor navigation must span the full body: {:#?}",
12270            parsed.navigation_ranges
12271        );
12272        assert_eq!(
12273            parsed
12274                .signature_metadata
12275                .get(constructor)
12276                .and_then(|metadata| metadata.first())
12277                .and_then(SignatureMetadata::callable_linkage),
12278            Some(CallableLinkage::External)
12279        );
12280        let token_class = parsed
12281            .declarations()
12282            .iter()
12283            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
12284            .expect("recovered Token class");
12285        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
12286        assert!(
12287            parsed
12288                .navigation_ranges
12289                .get(token_class)
12290                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
12291            "class navigation must include the terminating semicolon: {:#?}",
12292            parsed.navigation_ranges
12293        );
12294    }
12295
12296    #[test]
12297    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
12298        let source = r#"
12299#define SIMPLECPP_LIB
12300namespace simplecpp {
12301using TokenString = std::string;
12302class Macro;
12303struct Location {
12304  unsigned int fileIndex{};
12305  unsigned int line{};
12306  unsigned int col{};
12307};
12308struct Output {
12309  int type;
12310};
12311class SIMPLECPP_LIB Token {
12312 public:
12313  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12314      whitespaceahead(wsahead), location(loc), string(s) {
12315      flags();
12316  }
12317  Token(const Token &tok) :
12318      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
12319      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
12320      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
12321  Token &operator=(const Token &tok) = delete;
12322  const TokenString& str() const { return string; }
12323  void setstr(const std::string &s) { string = s; flags(); }
12324  bool isOneOf(const char ops[]) const;
12325  TokenString macro;
12326  char op;
12327  bool comment;
12328  bool name;
12329  bool number;
12330  bool whitespaceahead;
12331  Location location;
12332  Token *previous{};
12333  Token *next{};
12334 private:
12335  void flags() {
12336      name = !string.empty();
12337      comment = false;
12338      number = false;
12339      op = 0;
12340  }
12341  TokenString string;
12342};
12343}
12344struct Following {
12345  int type;
12346};
12347class SIMPLECPP_LIB Later {
12348 public:
12349  Later(int value) : value(value) {}
12350  int value;
12351};
12352"#;
12353        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
12354        assert!(
12355            parsed
12356                .declarations()
12357                .iter()
12358                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
12359        );
12360        assert!(
12361            !parsed
12362                .declarations()
12363                .iter()
12364                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
12365        );
12366        assert!(
12367            parsed
12368                .declarations()
12369                .iter()
12370                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
12371        );
12372        assert!(
12373            !parsed
12374                .declarations()
12375                .iter()
12376                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12377        );
12378        assert!(
12379            parsed
12380                .declarations()
12381                .iter()
12382                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
12383        );
12384        assert!(
12385            parsed
12386                .declarations()
12387                .iter()
12388                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
12389        );
12390        assert!(
12391            parsed
12392                .declarations()
12393                .iter()
12394                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
12395        );
12396        assert!(
12397            parsed
12398                .declarations()
12399                .iter()
12400                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
12401        );
12402        assert!(
12403            parsed
12404                .declarations()
12405                .iter()
12406                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
12407        );
12408        assert!(
12409            parsed
12410                .declarations()
12411                .iter()
12412                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
12413        );
12414        assert!(parsed.declarations().iter().all(|unit| {
12415            !matches!(
12416                unit.fq_name().as_str(),
12417                "simplecpp.Token.Following" | "simplecpp.Token.Later"
12418            )
12419        }));
12420        assert!(
12421            !parsed
12422                .declarations()
12423                .iter()
12424                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
12425            "the following struct must remain outside the recovered Token class"
12426        );
12427    }
12428
12429    #[test]
12430    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
12431        let source = r#"
12432#define SIMPLECPP_LIB
12433namespace {
12434namespace simplecpp {
12435using TokenString = std::string;
12436struct Location { int line{}; };
12437class SIMPLECPP_LIB HiddenToken {
12438 public:
12439  HiddenToken(const TokenString &s, const Location &loc) :
12440      location(loc), string(s) {
12441      flags();
12442  }
12443  TokenString string;
12444  Location location;
12445  HiddenToken *previous{};
12446 private:
12447  void flags() {}
12448};
12449}
12450}
12451"#;
12452        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
12453        let constructor = parsed
12454            .declarations()
12455            .iter()
12456            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
12457            .expect("recovered anonymous-namespace constructor");
12458        assert_eq!(
12459            parsed
12460                .signature_metadata
12461                .get(constructor)
12462                .and_then(|metadata| metadata.first())
12463                .and_then(SignatureMetadata::callable_linkage),
12464            Some(CallableLinkage::Internal)
12465        );
12466    }
12467
12468    #[test]
12469    fn macro_qualified_static_field_keeps_real_declarator() {
12470        let source = r#"#define JSON_INLINE_VARIABLE
12471struct Reader {
12472static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
12473static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
12474static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
12475};"#;
12476        let mut parser = tree_sitter::Parser::new();
12477        parser
12478            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12479            .unwrap();
12480        let tree = parser.parse(source, None).unwrap();
12481        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
12482        let parsed = parse_cpp_file(&file, source, &tree);
12483        for expected in [
12484            "Reader.npos",
12485            "Reader.other",
12486            "Reader.pointer",
12487            "Reader.reference",
12488        ] {
12489            assert!(
12490                parsed
12491                    .declarations()
12492                    .iter()
12493                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
12494                "real macro-decorated field {expected} is missing: {:#?}",
12495                parsed.declarations()
12496            );
12497        }
12498        assert!(
12499            parsed
12500                .declarations()
12501                .iter()
12502                .all(|unit| unit.fq_name() != "Reader.std"),
12503            "qualified type prefix became a pseudo-field: {:#?}",
12504            parsed.declarations()
12505        );
12506        let root = tree.root_node();
12507        let mut stack = vec![root];
12508        let mut signatures = Vec::new();
12509        while let Some(current) = stack.pop() {
12510            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
12511            {
12512                signatures.extend(
12513                    declarators
12514                        .into_iter()
12515                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
12516                );
12517            }
12518            let mut cursor = current.walk();
12519            stack.extend(current.named_children(&mut cursor));
12520        }
12521        signatures.sort();
12522        assert_eq!(
12523            signatures,
12524            [
12525                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
12526                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
12527                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
12528                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
12529            ]
12530        );
12531    }
12532
12533    fn member_function_linkage(source: &str) -> CallableLinkage {
12534        let mut parser = tree_sitter::Parser::new();
12535        parser
12536            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12537            .unwrap();
12538        let tree = parser.parse(source, None).unwrap();
12539        let mut stack = vec![tree.root_node()];
12540        while let Some(node) = stack.pop() {
12541            if node.kind() == "function_definition" {
12542                let mut current = node.parent();
12543                while let Some(parent) = current {
12544                    if matches!(
12545                        parent.kind(),
12546                        "class_specifier" | "struct_specifier" | "union_specifier"
12547                    ) {
12548                        return cpp_callable_linkage(node, source);
12549                    }
12550                    current = parent.parent();
12551                }
12552            }
12553            let mut cursor = node.walk();
12554            stack.extend(node.named_children(&mut cursor));
12555        }
12556        panic!("fixture has no member function definition");
12557    }
12558
12559    #[test]
12560    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
12561        assert_eq!(
12562            member_function_linkage("struct Named { int method() { return 1; } };"),
12563            CallableLinkage::External
12564        );
12565        assert_eq!(
12566            member_function_linkage(
12567                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
12568            ),
12569            CallableLinkage::Internal
12570        );
12571        assert_eq!(
12572            member_function_linkage("struct { int method() { return 1; } } instance;"),
12573            CallableLinkage::Internal
12574        );
12575        assert_eq!(
12576            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
12577            CallableLinkage::Internal
12578        );
12579    }
12580
12581    #[test]
12582    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12583        let source = r#"
12584#ifndef PROTON_VALUE_HPP
12585#define PROTON_VALUE_HPP
12586namespace proton {
12587namespace internal {
12588class value_base {
12589  protected:
12590    internal::data& data();
12591    internal::data data_;
12592  friend class codec::encoder;
12593  friend class codec::decoder;
12594};
12595}
12596class value : public internal::value_base, private internal::comparable<value> {
12597  private:
12598    template<class T, class U=void> struct assignable :
12599        public std::enable_if<codec::is_encodable<T>::value, U> {};
12600    template<class U> struct assignable<value, U> {};
12601  public:
12602    PN_CPP_EXTERN value();
12603    PN_CPP_EXTERN value(const value&);
12604    PN_CPP_EXTERN value& operator=(const value&);
12605    PN_CPP_EXTERN value(value&&);
12606    PN_CPP_EXTERN value& operator=(value&&);
12607    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12608    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12609        codec::encoder e(*this);
12610        e << x;
12611        return *this;
12612    }
12613    PN_CPP_EXTERN type_id type() const;
12614    PN_CPP_EXTERN bool empty() const;
12615    PN_CPP_EXTERN void clear();
12616    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12617    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12618  friend PN_CPP_EXTERN void swap(value&, value&);
12619  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
12620  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
12621  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
12622    value(pn_data_t* d);
12623    void reset(pn_data_t* d = 0);
12624};
12625}
12626#endif
12627"#;
12628        let mut parser = tree_sitter::Parser::new();
12629        parser
12630            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12631            .unwrap();
12632        let tree = parser.parse(source, None).unwrap();
12633        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
12634        let parsed = parse_cpp_file(&file, source, &tree);
12635        let macro_constructors = parsed
12636            .signature_metadata
12637            .iter()
12638            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
12639            .flat_map(|(_, metadata)| metadata)
12640            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
12641            .collect::<Vec<_>>();
12642
12643        assert_eq!(
12644            macro_constructors.len(),
12645            3,
12646            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
12647            parsed.declarations()
12648        );
12649        assert!(
12650            macro_constructors.iter().all(|metadata| {
12651                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
12652            }),
12653            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
12654        );
12655    }
12656
12657    #[test]
12658    fn recovered_export_class_typedef_uses_displaced_alias_name() {
12659        let source = r#"
12660namespace spi {
12661class Filter {
12662public:
12663    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
12664};
12665}
12666namespace filter {
12667class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
12668{
12669public:
12670    typedef spi::Filter BASE_CLASS;
12671    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
12672    BEGIN_LOG4CXX_CAST_MAP()
12673    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
12674    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
12675    END_LOG4CXX_CAST_MAP()
12676    FilterDecision decide() const;
12677};
12678}
12679"#;
12680        let mut parser = tree_sitter::Parser::new();
12681        parser
12682            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12683            .unwrap();
12684        let tree = parser.parse(source, None).unwrap();
12685        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
12686        let parsed = parse_cpp_file(&file, source, &tree);
12687        assert!(
12688            parsed.declarations().iter().any(|unit| {
12689                unit.is_class()
12690                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
12691                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
12692            }),
12693            "the displaced typedef alias must retain its declared name: {:#?}",
12694            parsed.declarations()
12695        );
12696        assert!(
12697            parsed
12698                .declarations()
12699                .iter()
12700                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
12701            "the qualified underlying type must not become a false nested alias: {:#?}",
12702            parsed.declarations()
12703        );
12704    }
12705
12706    #[test]
12707    fn exported_single_base_recovery_uses_displaced_class_name() {
12708        let source = r#"
12709class CORE_EXPORT QgsPoint : public AbstractGeometry
12710{
12711    Q_GADGET
12712
12713    Q_PROPERTY( double x READ x WRITE setX )
12714    Q_PROPERTY( double y READ y WRITE setY )
12715    Q_PROPERTY( double z READ z WRITE setZ )
12716    Q_PROPERTY( double m READ m WRITE setM )
12717
12718  public:
12719#ifndef SIP_RUN
12720    QgsPoint(
12721      double x = std::numeric_limits<double>::quiet_NaN(),
12722      double y = std::numeric_limits<double>::quiet_NaN(),
12723      double z = std::numeric_limits<double>::quiet_NaN(),
12724      double m = std::numeric_limits<double>::quiet_NaN(),
12725      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
12726    );
12727#else
12728    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 )];
12729    % MethodCode
12730    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12731    {
12732      int state;
12733      sipIsErr = 0;
12734      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12735      if ( !sipIsErr )
12736      {
12737        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12738      }
12739      sipReleaseType( p, sipType_QgsPointXY, state );
12740    }
12741    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
12742    {
12743      int state;
12744      sipIsErr = 0;
12745
12746      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
12747      if ( !sipIsErr )
12748      {
12749        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
12750      }
12751      sipReleaseType( p, sipType_QPointF, state );
12752    }
12753    else if (
12754      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
12755      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
12756      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
12757      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
12758    {
12759      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
12760      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
12761      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
12762      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
12763      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
12764      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
12765    }
12766    else // Invalid ctor arguments
12767    {
12768      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
12769      sipIsErr = 1;
12770    }
12771    % End
12772#endif
12773
12774    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
12775    explicit QgsPoint( QPointF p ) SIP_SKIP;
12776    explicit QgsPoint(
12777      Qgis::WkbType wkbType,
12778      double x = std::numeric_limits<double>::quiet_NaN(),
12779      double y = std::numeric_limits<double>::quiet_NaN(),
12780      double z = std::numeric_limits<double>::quiet_NaN(),
12781      double m = std::numeric_limits<double>::quiet_NaN()
12782    ) SIP_SKIP;
12783    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12784    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
12785    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
12786#ifndef SIP_RUN
12787  private:
12788    bool fuzzyHelper(
12789      double epsilon,
12790      const AbstractGeometry &other,
12791      bool is3DFlag,
12792      bool isMeasureFlag
12793    ) const
12794    {
12795      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
12796    }
12797#endif
12798};
12799class Ordinary : public Base { public: Ordinary(); };
12800class API_EXPORT Plain { public: Plain(); };
12801class API_EXPORT : public Base {};
12802class
12803PN_CPP_CLASS_EXTERN Sender : public Link {
12804    Sender();
12805};
12806class thread_ctx_t {};
12807class ctx_t ZMQ_FINAL : public thread_ctx_t {
12808    bool start();
12809};
12810"#;
12811        let mut parser = tree_sitter::Parser::new();
12812        parser
12813            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12814            .unwrap();
12815        let tree = parser.parse(source, None).unwrap();
12816        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
12817        let parsed = parse_cpp_file(&file, source, &tree);
12818        let declarations = parsed.declarations();
12819
12820        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
12821            assert!(
12822                declarations
12823                    .iter()
12824                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
12825                "missing recovered class {expected}: {declarations:#?}"
12826            );
12827        }
12828        let qgs_point = declarations
12829            .iter()
12830            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
12831            .expect("recovered QgsPoint class");
12832        assert_eq!(
12833            parsed.raw_supertypes.get(qgs_point),
12834            Some(&vec!["AbstractGeometry".to_string()]),
12835            "single-base export recovery must retain its displaced base"
12836        );
12837        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
12838        assert!(
12839            parsed
12840                .navigation_ranges
12841                .get(qgs_point)
12842                .is_some_and(|ranges| {
12843                    !ranges.is_empty()
12844                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
12845                }),
12846            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
12847            parsed.navigation_ranges.get(qgs_point)
12848        );
12849        let sender = declarations
12850            .iter()
12851            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
12852            .expect("recovered Sender class");
12853        assert_eq!(
12854            parsed.raw_supertypes.get(sender),
12855            Some(&vec!["Link".to_string()]),
12856            "post-declarator export recovery must retain its displaced base"
12857        );
12858        let ctx = declarations
12859            .iter()
12860            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
12861            .expect("recovered ctx_t class");
12862        assert_eq!(
12863            parsed.raw_supertypes.get(ctx),
12864            Some(&vec!["thread_ctx_t".to_string()]),
12865            "postfix export-macro recovery must retain its displaced base"
12866        );
12867        assert!(
12868            declarations.iter().any(|unit| {
12869                unit.is_function()
12870                    && unit.fq_name() == "QgsPoint.QgsPoint"
12871                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
12872            }),
12873            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
12874        );
12875        assert!(
12876            declarations.iter().all(|unit| {
12877                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
12878            }),
12879            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
12880        );
12881    }
12882
12883    #[test]
12884    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
12885        let positive_source =
12886            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
12887        let mut parser = tree_sitter::Parser::new();
12888        parser
12889            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12890            .unwrap();
12891        let positive_tree = parser.parse(positive_source, None).unwrap();
12892        assert!(cpp_reparsed_members_are_indexable(
12893            positive_tree.root_node(),
12894            positive_source
12895        ));
12896
12897        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
12898        let negative_tree = parser.parse(negative_source, None).unwrap();
12899        assert!(!cpp_reparsed_members_are_indexable(
12900            negative_tree.root_node(),
12901            negative_source
12902        ));
12903    }
12904
12905    #[test]
12906    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
12907        let copy_control_source = r#"
12908public:
12909    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
12910    explicit Token(const Token* tok);
12911    ~Token();
12912    Token* astOperand1() { return nullptr; }
12913"#;
12914        let constraint_source = r#"
12915private:
12916    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12917    static T *tokAtImpl(T *tok, int index) {
12918        return tok;
12919    }
12920
12921    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
12922    static T *linkAtImpl(T *tok, int index) {
12923        return tok;
12924    }
12925
12926public:
12927    int late() const { return 1; }
12928"#;
12929        let mut parser = tree_sitter::Parser::new();
12930        parser
12931            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12932            .unwrap();
12933        let copy_control_tree = parser
12934            .parse(copy_control_source, None)
12935            .expect("parse copy-control fixture");
12936        assert!(
12937            copy_control_tree.root_node().has_error(),
12938            "fixture must exercise adjacent copy-control recovery"
12939        );
12940        assert!(
12941            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
12942            "a complete late getter must remain recoverable after adjacent copy-control declarations"
12943        );
12944        let mut cursor = copy_control_tree.root_node().walk();
12945        assert!(
12946            copy_control_tree
12947                .root_node()
12948                .named_children(&mut cursor)
12949                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
12950            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
12951            copy_control_tree.root_node().to_sexp()
12952        );
12953        let constraint_tree = parser
12954            .parse(constraint_source, None)
12955            .expect("parse constraint-macro fixture");
12956        assert!(constraint_tree.root_node().has_error());
12957        assert!(
12958            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
12959            "complete constraint-macro members must not hide a later ordinary member"
12960        );
12961        let mut cursor = constraint_tree.root_node().walk();
12962        assert!(
12963            constraint_tree
12964                .root_node()
12965                .named_children(&mut cursor)
12966                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
12967                    child,
12968                    constraint_source
12969                )),
12970            "fixture must retain the split constraint-macro prefix/function geometry"
12971        );
12972    }
12973
12974    #[test]
12975    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
12976        let source = r#"
12977struct Analyzer {
12978    struct Action {
12979        Action() = default;
12980        Action(const Action&) = default;
12981        Action& operator=(const Action& rhs) & = default;
12982
12983        template<class T,
12984                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
12985                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
12986        // NOLINTNEXTLINE(google-explicit-constructor)
12987        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
12988        {}
12989
12990        enum : std::uint16_t { None = 0, Read = (1 << 0) };
12991        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
12992
12993    private:
12994        unsigned int mFlag{};
12995    };
12996
12997    enum class Direction : unsigned char { Forward, Reverse };
12998    virtual Action analyze(Direction d) const = 0;
12999};
13000"#;
13001        let mut parser = tree_sitter::Parser::new();
13002        parser
13003            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13004            .unwrap();
13005        let tree = parser.parse(source, None).unwrap();
13006        assert!(tree.root_node().has_error());
13007        let root = tree.root_node();
13008        let outer = root
13009            .named_children(&mut root.walk())
13010            .find(|child| child.kind() == "ERROR")
13011            .expect("fragmented Analyzer prefix");
13012        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
13013            .expect("structured Analyzer fragment boundary");
13014        assert_eq!(outer_name, "Analyzer");
13015        let outer_tree = cpp_reparse_fragmented_class_body(
13016            source,
13017            outer_fragment.reparse_start,
13018            outer_fragment.reparse_end,
13019        )
13020        .expect("reparse Analyzer body");
13021        let outer_root = outer_tree.root_node();
13022        let action_prefix = outer_root
13023            .named_children(&mut outer_root.walk())
13024            .find(|child| child.kind() == "ERROR")
13025            .expect("fragmented Action prefix");
13026        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
13027            .expect("structured Action fragment boundary");
13028        assert_eq!(action_name, "Action");
13029        let action_tree = cpp_reparse_fragmented_class_body(
13030            source,
13031            action_fragment.reparse_start,
13032            action_fragment.reparse_end,
13033        )
13034        .expect("reparse Action body");
13035        let action_root = action_tree.root_node();
13036        let macro_prefix = action_root
13037            .named_children(&mut action_root.walk())
13038            .find(|child| child.kind() == "ERROR")
13039            .expect("constraint macro prefix");
13040        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
13041            .expect("structured template macro prefix");
13042        let macro_companion =
13043            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
13044        assert!(
13045            cpp_reparsed_template_macro_constructor_companion_is_indexable(
13046                macro_companion,
13047                macro_parameter,
13048                source,
13049            ),
13050            "split constrained constructor must be admitted: {}",
13051            macro_companion.to_sexp()
13052        );
13053        assert!(
13054            cpp_reparsed_members_are_indexable(action_root, source),
13055            "complete Action body must pass the recovery gate: {}",
13056            action_tree.root_node().to_sexp()
13057        );
13058        assert!(
13059            cpp_reparsed_members_are_indexable(outer_root, source),
13060            "complete Analyzer body must pass the recovery gate: {}",
13061            outer_tree.root_node().to_sexp()
13062        );
13063        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
13064        let parsed = parse_cpp_file(&file, source, &tree);
13065        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
13066            assert!(
13067                parsed
13068                    .declarations()
13069                    .iter()
13070                    .any(|unit| unit.fq_name() == expected),
13071                "missing recovered declaration {expected}: {:#?}",
13072                parsed.declarations()
13073            );
13074        }
13075        assert!(
13076            parsed
13077                .declarations()
13078                .iter()
13079                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
13080            "nested members must not remain flattened: {:#?}",
13081            parsed.declarations()
13082        );
13083    }
13084
13085    #[test]
13086    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
13087        let source = r#"
13088raw_hash_set& operator=(raw_hash_set&& that) {
13089  return move_assign(
13090      std::move(that),
13091      typename AllocTraits::propagate_on_container_move_assignment());
13092}
13093
13094iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
13095  return {};
13096}
13097
13098void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
13099
13100iterator insert(const_iterator hint, value_type&& value)
13101    ABSL_ATTRIBUTE_LIFETIME_BOUND {
13102  return {};
13103}
13104
13105friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
13106  return left.size() == right.size();
13107}
13108
13109static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
13110  return static_cast<slot_type*>(buffer);
13111}
13112
13113protected:
13114// Included-range recovery can attach this comment to the template prefix.
13115template <class K>
13116void AssertOnFind([[maybe_unused]] const K& key) {
13117  Check(key);
13118}
13119"#;
13120        let mut parser = tree_sitter::Parser::new();
13121        parser
13122            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13123            .unwrap();
13124        let tree = parser.parse(source, None).unwrap();
13125        assert!(
13126            tree.root_node().has_error(),
13127            "the fixture must exercise tree-sitter's errorful member shapes"
13128        );
13129        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
13130
13131        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
13132        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
13133        assert!(!cpp_reparsed_members_are_indexable(
13134            incomplete_tree.root_node(),
13135            incomplete_source
13136        ));
13137
13138        let outside_error_source = "int foo() stray_attribute {}\n";
13139        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
13140        assert!(outside_error_tree.root_node().has_error());
13141        assert!(!cpp_reparsed_members_are_indexable(
13142            outside_error_tree.root_node(),
13143            outside_error_source
13144        ));
13145
13146        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
13147        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13148        assert!(!cpp_reparsed_members_are_indexable(
13149            variable_initializer_tree.root_node(),
13150            variable_initializer_source
13151        ));
13152    }
13153
13154    #[test]
13155    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
13156        let positive_source = r#"
13157std::pair<iterator, bool> insert(init_type&& value)
13158    ABSL_ATTRIBUTE_LIFETIME_BOUND
13159#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13160  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13161#endif
13162{
13163  return emplace(std::move(value));
13164}
13165"#;
13166        let mut parser = tree_sitter::Parser::new();
13167        parser
13168            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13169            .unwrap();
13170        let positive_tree = parser.parse(positive_source, None).unwrap();
13171        assert!(
13172            positive_tree.root_node().has_error(),
13173            "the fixture must exercise the split attribute/requires shape"
13174        );
13175        assert!(cpp_reparsed_members_are_indexable(
13176            positive_tree.root_node(),
13177            positive_source
13178        ));
13179
13180        let template_return_source = r#"
13181pair<int> insert(init_type&& value)
13182    ABSL_ATTRIBUTE_LIFETIME_BOUND
13183#if LANGUAGE_LEVEL >= 202002L
13184  requires(!Predicate<init_type>::value)
13185#endif
13186// Attributes and the function body may be separated by comments.
13187{
13188  return {};
13189}
13190"#;
13191        let template_return_tree = parser.parse(template_return_source, None).unwrap();
13192        assert!(
13193            cpp_reparsed_members_are_indexable(
13194                template_return_tree.root_node(),
13195                template_return_source
13196            ),
13197            "template-return attribute/requires tree: {}",
13198            template_return_tree.root_node().to_sexp()
13199        );
13200
13201        let no_body_source = r#"
13202std::pair<iterator, bool> insert(init_type&& value)
13203    ABSL_ATTRIBUTE_LIFETIME_BOUND
13204#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13205  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13206#endif
13207+ 0;
13208"#;
13209        let no_body_tree = parser.parse(no_body_source, None).unwrap();
13210        assert!(!cpp_reparsed_members_are_indexable(
13211            no_body_tree.root_node(),
13212            no_body_source
13213        ));
13214
13215        let extra_payload_source = r#"
13216pair<int> insert(init_type&& value)
13217    ABSL_ATTRIBUTE_LIFETIME_BOUND
13218#if LANGUAGE_LEVEL >= 202002L
13219  int unrelated;
13220  requires(Predicate<init_type>::value)
13221#endif
13222{
13223  return {};
13224}
13225"#;
13226        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
13227        assert!(!cpp_reparsed_members_are_indexable(
13228            extra_payload_tree.root_node(),
13229            extra_payload_source
13230        ));
13231
13232        let variable_initializer_source = r#"
13233int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
13234#if LANGUAGE_LEVEL >= 202002L
13235  requires(true)
13236#endif
13237{
13238  bad;
13239}
13240"#;
13241        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13242        assert!(!cpp_reparsed_members_are_indexable(
13243            variable_initializer_tree.root_node(),
13244            variable_initializer_source
13245        ));
13246    }
13247
13248    #[test]
13249    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
13250        let source = r#"namespace absl {
13251ABSL_NAMESPACE_BEGIN namespace container_internal {
13252
13253class raw_hash_set : public Base {
13254 public:
13255  using value_type = int;
13256
13257  template <class U,
13258            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
13259  void insert(U value) { (void)value; }
13260
13261  struct InsertSlot {
13262    raw_hash_set& s;
13263  };
13264};
13265
13266}
13267ABSL_NAMESPACE_END
13268}"#;
13269        let mut parser = tree_sitter::Parser::new();
13270        parser
13271            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13272            .unwrap();
13273        let tree = parser.parse(source, None).unwrap();
13274        let root = tree.root_node();
13275        let outer_namespace = root
13276            .named_children(&mut root.walk())
13277            .find(|child| child.kind() == "namespace_definition")
13278            .expect("outer absl namespace");
13279        let declaration_list = outer_namespace
13280            .child_by_field_name("body")
13281            .expect("outer namespace body");
13282        let sentinel_function = declaration_list
13283            .named_children(&mut declaration_list.walk())
13284            .find(|child| child.kind() == "function_definition")
13285            .expect("malformed namespace sentinel function");
13286        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source)
13287            .expect("structured nested namespace sentinel");
13288        let fragmented =
13289            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source)
13290                .expect("fragmented raw_hash_set class");
13291        assert_eq!(fragmented.class_node.kind(), "ERROR");
13292        assert_eq!(fragmented.name, "raw_hash_set");
13293        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
13294
13295        let outer_scope =
13296            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
13297        let mut outer_siblings = Vec::new();
13298        push_cpp_sentinel_sibling_classes(
13299            &mut outer_siblings,
13300            declaration_list,
13301            sentinel.function,
13302            &outer_scope,
13303            source,
13304        );
13305        let [outer_shadow] = outer_siblings.as_slice() else {
13306            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
13307        };
13308        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
13309        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
13310
13311        let field = "    raw_hash_set& s;";
13312        let start = source.find(field).expect("InsertSlot field") + 4;
13313        let node = root
13314            .descendant_for_byte_range(start, start + "raw_hash_set".len())
13315            .expect("raw_hash_set type node");
13316        let recovered = cpp_sentinel_recovered_classes(root, source);
13317        let [deep_class] = recovered.as_slice() else {
13318            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
13319        };
13320        assert_eq!(
13321            deep_class.namespace_scope_components,
13322            vec!["absl", "container_internal"]
13323        );
13324        assert_eq!(
13325            deep_class.scope_components,
13326            vec!["absl", "container_internal", "raw_hash_set"]
13327        );
13328        assert!(
13329            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
13330                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
13331        );
13332
13333        assert_eq!(
13334            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
13335            Some(vec![
13336                "absl".to_string(),
13337                "container_internal".to_string(),
13338                "raw_hash_set".to_string(),
13339                "InsertSlot".to_string(),
13340            ])
13341        );
13342
13343        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
13344        let parsed = parse_cpp_file(&file, source, &tree);
13345        let raw_hash_set = parsed
13346            .declarations()
13347            .iter()
13348            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
13349            .expect("recovered raw_hash_set class");
13350        assert_eq!(
13351            raw_hash_set.fq_name(),
13352            "absl::container_internal.raw_hash_set",
13353            "the recovered declaration must publish under the deeper sentinel namespace"
13354        );
13355        assert_eq!(
13356            parsed.raw_supertypes.get(raw_hash_set),
13357            Some(&vec!["Base".to_string()]),
13358            "the structured base clause on the fragmented ERROR prefix must survive publication"
13359        );
13360        assert!(
13361            parsed.materialization_records.iter().any(|record| matches!(
13362                record,
13363                MaterializationRecord::RecoveredDeclaration { recovery, unit }
13364                    if unit == raw_hash_set && *recovery == deep_class.class_range
13365            )),
13366            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
13367            parsed.materialization_records
13368        );
13369    }
13370
13371    #[test]
13372    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
13373        const DISTINCT_PER_KIND: usize = 64;
13374        let mut source = String::new();
13375        for index in 0..DISTINCT_PER_KIND {
13376            writeln!(source, "typedef int Alias{index};").unwrap();
13377        }
13378        writeln!(source, "typedef long Alias0;").unwrap();
13379        for index in 0..DISTINCT_PER_KIND {
13380            writeln!(source, "#define MACRO_{index} {index}").unwrap();
13381        }
13382        writeln!(source, "#define MACRO_0 duplicate").unwrap();
13383        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
13384
13385        let mut parser = tree_sitter::Parser::new();
13386        parser
13387            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13388            .unwrap();
13389        let tree = parser.parse(&source, None).unwrap();
13390        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
13391
13392        start_declaration_identity_comparison_probe();
13393        let parsed = parse_cpp_file(&file, &source, &tree);
13394        let comparisons = finish_declaration_identity_comparison_probe();
13395
13396        assert_eq!(
13397            DISTINCT_PER_KIND + 1,
13398            parsed
13399                .declarations()
13400                .iter()
13401                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
13402                .count(),
13403            "every physical typedef alias declaration must be retained so \
13404             conditional branch guards stay available to the resolver"
13405        );
13406        assert_eq!(
13407            DISTINCT_PER_KIND,
13408            parsed
13409                .declarations()
13410                .iter()
13411                .filter(|unit| {
13412                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
13413                })
13414                .count(),
13415            "macros should retain semantic-identity deduplication"
13416        );
13417        assert_eq!(
13418            2,
13419            parsed
13420                .declarations()
13421                .iter()
13422                .filter(|unit| {
13423                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
13424                })
13425                .count(),
13426            "function overloads must remain distinct"
13427        );
13428
13429        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
13430        assert!(
13431            comparisons <= dedup_inputs * 4,
13432            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
13433        );
13434    }
13435
13436    #[test]
13437    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
13438        let source = r#"namespace absl {
13439ABSL_NAMESPACE_BEGIN namespace container_internal {
13440template <typename T>
13441class broken {
13442 public:
13443  using value_type = T;
13444  T operator->() const { return &operator*(); }
13445  using alias = value_type;
13446};
13447}
13448}
13449"#;
13450        let mut parser = tree_sitter::Parser::new();
13451        parser
13452            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13453            .unwrap();
13454        let tree = parser.parse(source, None).unwrap();
13455        let broken = find_class_named(tree.root_node(), source, "broken")
13456            .expect("the positive fixture must expose the broken class node");
13457        assert!(
13458            broken.has_error(),
13459            "the positive fixture must retain an internal parser error"
13460        );
13461        assert!(
13462            cpp_complete_class_body_close(broken).is_some(),
13463            "the positive fixture must expose a real class body close"
13464        );
13465        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13466        assert!(
13467            recovered.iter().any(|class| {
13468                class.scope_components == ["absl", "container_internal", "broken"]
13469            }),
13470            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
13471        );
13472    }
13473
13474    #[test]
13475    fn sentinel_recovery_keeps_members_after_nested_body_close() {
13476        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
13477NLOHMANN_BASIC_JSON_TPL_DECLARATION
13478class basic_json {
13479 private:
13480  union storage {
13481    int value;
13482  } data;
13483 public:
13484  using late_alias = int;
13485  late_alias value() const;
13486};
13487NLOHMANN_JSON_NAMESPACE_END
13488"#;
13489        let mut parser = tree_sitter::Parser::new();
13490        parser
13491            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13492            .unwrap();
13493        let tree = parser.parse(source, None).unwrap();
13494        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13495        let basic_json = recovered
13496            .iter()
13497            .find(|class| {
13498                class
13499                    .scope_components
13500                    .last()
13501                    .is_some_and(|name| name == "basic_json")
13502            })
13503            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
13504        let late_alias = source
13505            .find("late_alias value")
13506            .expect("late alias reference");
13507        assert!(
13508            basic_json.class_range.start_byte < late_alias
13509                && late_alias < basic_json.class_range.end_byte,
13510            "the recovered class range must include members after a nested close: {basic_json:#?}"
13511        );
13512    }
13513
13514    #[test]
13515    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
13516        let source = r#"namespace absl {
13517ABSL_NAMESPACE_BEGIN namespace container_internal {
13518template <typename T>
13519class broken {
13520 public:
13521  using value_type = T;
13522  T operator->() const { return &operator*(); }
13523}
13524}
13525"#;
13526        let mut parser = tree_sitter::Parser::new();
13527        parser
13528            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13529            .unwrap();
13530        let tree = parser.parse(source, None).unwrap();
13531        let broken = find_class_named(tree.root_node(), source, "broken")
13532            .expect("the negative fixture must expose the malformed class node");
13533        assert!(
13534            broken.has_error(),
13535            "the negative fixture must retain a parser error"
13536        );
13537        assert!(
13538            cpp_complete_class_body_close(broken).is_none(),
13539            "the malformed class must not expose a real body close"
13540        );
13541        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13542        assert!(
13543            recovered
13544                .iter()
13545                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
13546            "an incomplete class must not borrow the namespace close: {recovered:#?}"
13547        );
13548    }
13549
13550    #[test]
13551    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
13552        let source = r#"namespace absl {
13553ABSL_NAMESPACE_BEGIN namespace container_internal {
13554template <typename T>
13555struct broken {
13556  using value_type = T;
13557};
13558}
13559
13560#ifdef OWNER_DEF
13561template <typename T>
13562typename broken<T>::value_type broken<T>::method() {
13563  value_type value{};
13564  return value;
13565}
13566#endif
13567
13568namespace sibling {
13569template <typename T>
13570typename broken<T>::value_type broken<T>::other() {
13571  value_type value{};
13572  return value;
13573}
13574}
13575
13576ABSL_NAMESPACE_END
13577}
13578"#;
13579        let mut parser = tree_sitter::Parser::new();
13580        parser
13581            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13582            .unwrap();
13583        let tree = parser.parse(source, None).unwrap();
13584        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13585        let broken = recovered
13586            .iter()
13587            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13588            .expect("the sentinel class must be recovered");
13589        let method_start = source
13590            .find("typename broken<T>::value_type broken<T>::method()")
13591            .expect("guarded sibling owner");
13592        let method_end = source[method_start..]
13593            .find("\n}")
13594            .map(|offset| method_start + offset + 2)
13595            .expect("guarded sibling owner close");
13596        assert!(
13597            broken
13598                .owner_ranges
13599                .iter()
13600                .any(|owner| owner.range.start_byte <= method_start
13601                    && method_end <= owner.range.end_byte),
13602            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
13603        );
13604        let sibling_start = source
13605            .find("typename broken<T>::value_type broken<T>::other()")
13606            .expect("nested namespace sibling owner");
13607        assert!(
13608            broken
13609                .owner_ranges
13610                .iter()
13611                .all(|owner| owner.range.start_byte > sibling_start
13612                    || owner.range.end_byte <= sibling_start),
13613            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
13614        );
13615    }
13616
13617    #[test]
13618    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
13619        let source = r#"#ifdef OUTER
13620namespace absl {
13621ABSL_NAMESPACE_BEGIN namespace container_internal {
13622template <typename T>
13623struct broken {
13624  using value_type = T;
13625};
13626}
13627}
13628
13629#ifdef OWNER_DEF
13630template <typename T>
13631typename broken<T>::value_type broken<T>::method() {
13632  value_type value{};
13633  return value;
13634}
13635#endif
13636#endif
13637"#;
13638        let mut parser = tree_sitter::Parser::new();
13639        parser
13640            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13641            .unwrap();
13642        let tree = parser.parse(source, None).unwrap();
13643        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13644        let broken = recovered
13645            .iter()
13646            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
13647            .expect("the sentinel class must be recovered");
13648        let method_start = source
13649            .find("typename broken<T>::value_type broken<T>::method()")
13650            .expect("outer sibling owner");
13651        assert!(
13652            broken
13653                .owner_ranges
13654                .iter()
13655                .all(|owner| owner.range.start_byte > method_start
13656                    || owner.range.end_byte <= method_start),
13657            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
13658        );
13659    }
13660
13661    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
13662    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
13663        let mut signatures = parsed
13664            .declarations()
13665            .iter()
13666            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
13667            .filter_map(|unit| unit.signature().map(str::to_string))
13668            .collect::<Vec<_>>();
13669        signatures.sort();
13670        signatures.dedup();
13671        signatures
13672    }
13673
13674    #[test]
13675    fn callable_parameter_types_come_from_the_ast_parameter_list() {
13676        let source = r#"
13677template <typename T, ENABLE_BYTES(T)>
13678Vec256<T> DupOdd(Vec256<T> value) { return value; }
13679
13680struct Visitor {
13681  void fail(this auto const& self) {}
13682};
13683"#;
13684        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
13685        let dup_odd = parsed
13686            .declarations()
13687            .iter()
13688            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
13689            .expect("DupOdd declaration");
13690        assert_eq!(
13691            dup_odd.signature(),
13692            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
13693        );
13694        assert_eq!(
13695            parsed
13696                .signature_metadata
13697                .get(dup_odd)
13698                .and_then(|metadata| metadata.first())
13699                .and_then(SignatureMetadata::callable_parameter_types),
13700            Some(["Vec256<T>".to_string()].as_slice())
13701        );
13702
13703        let fail = parsed
13704            .declarations()
13705            .iter()
13706            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
13707            .expect("explicit-object member");
13708        assert_eq!(fail.signature(), Some("(const this auto &)"));
13709        let metadata = parsed
13710            .signature_metadata
13711            .get(fail)
13712            .and_then(|metadata| metadata.first())
13713            .expect("explicit-object signature metadata");
13714        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
13715        assert!(
13716            metadata
13717                .callable_arity()
13718                .is_some_and(|arity| arity.accepts(0))
13719        );
13720    }
13721
13722    #[test]
13723    fn trailing_qualifiers_survive_parameter_list_whitespace() {
13724        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
13725        // declarator's structure, so an out-of-line definition that spells its
13726        // parameter list with different whitespace than the declaration must
13727        // still carry it.
13728        let source = r#"
13729struct Widget {
13730  bool multiline(int settings, int supprs) const;
13731  bool doublespace(int settings, int supprs) const;
13732  bool noexcept_multiline(int settings, int supprs) noexcept;
13733  bool ref_multiline(int settings, int supprs) &&;
13734};
13735bool
13736Widget::multiline (int settings,
13737                   int supprs) const
13738{ return settings + supprs > 0; }
13739bool Widget::doublespace(int settings,  int supprs) const { return true; }
13740bool Widget::noexcept_multiline(int settings,
13741                                int supprs) noexcept { return true; }
13742bool Widget::ref_multiline(int settings,
13743                           int supprs) && { return true; }
13744"#;
13745        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
13746        assert_eq!(
13747            vec!["(int, int) const".to_string()],
13748            identity_signatures(&parsed, "Widget.multiline")
13749        );
13750        assert_eq!(
13751            vec!["(int, int) const".to_string()],
13752            identity_signatures(&parsed, "Widget.doublespace")
13753        );
13754        assert_eq!(
13755            vec!["(int, int) noexcept".to_string()],
13756            identity_signatures(&parsed, "Widget.noexcept_multiline")
13757        );
13758        assert_eq!(
13759            vec!["(int, int) &&".to_string()],
13760            identity_signatures(&parsed, "Widget.ref_multiline")
13761        );
13762    }
13763
13764    #[test]
13765    fn macro_fragmented_plain_class_keeps_following_member_signature() {
13766        let source = r#"
13767struct CString {};
13768class CMessage {
13769public:
13770  CString GetParams(unsigned int index, unsigned int length = -1) const
13771      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
13772    return GetParamsColon(index, length);
13773  }
13774  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
13775};
13776CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
13777  return {};
13778}
13779"#;
13780        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
13781        assert_eq!(
13782            vec!["(unsigned int, unsigned int) const".to_string()],
13783            identity_signatures(&parsed, "CMessage.GetParamsColon")
13784        );
13785    }
13786
13787    #[test]
13788    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
13789        let source = r#"
13790#pragma once
13791#define DEMO_DEPRECATED(message)
13792namespace demo {
13793struct Base {
13794    static int aligned(int value) { return value; }
13795    int legacy(int value) const
13796        DEMO_DEPRECATED("use replacement()") { return value; }
13797    int replacement() const;
13798    void run(int value);
13799};
13800struct OtherBase {
13801    void run(int value);
13802    static int aligned(int value) { return value; }
13803};
13804struct Derived : Base {};
13805struct Override : Base {
13806    void run(int value);
13807    static int aligned(int value) { return value; }
13808};
13809struct RecoveredOverride : Base {
13810    int legacy(int value) const
13811        DEMO_DEPRECATED("use replacement()") { return value; }
13812    void run(int value);
13813};
13814struct Hidden : Base {
13815    void run(int first, int second);
13816    static int aligned(int first, int second) { return first + second; }
13817};
13818struct Ambiguous : Base, OtherBase {};
13819}
13820struct Global {};
13821"#;
13822        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
13823        let declarations = parsed.declarations();
13824        let fq_names = declarations
13825            .iter()
13826            .map(|unit| unit.fq_name())
13827            .collect::<std::collections::BTreeSet<_>>();
13828
13829        for expected in [
13830            "demo.Base",
13831            "demo.Base.aligned",
13832            "demo.Base.legacy",
13833            "demo.Base.replacement",
13834            "demo.Base.run",
13835            "demo.Derived",
13836            "demo.OtherBase",
13837            "demo.Override",
13838            "demo.RecoveredOverride",
13839            "demo.Hidden",
13840            "demo.Ambiguous",
13841            "Global",
13842        ] {
13843            assert!(
13844                fq_names.contains(expected),
13845                "missing {expected} from namespaced macro fragment: {declarations:#?}"
13846            );
13847        }
13848        assert!(
13849            !fq_names.contains("Derived"),
13850            "following class escaped its namespace: {declarations:#?}"
13851        );
13852        assert!(
13853            !fq_names.contains("demo.Global"),
13854            "global class crossed the recovered namespace boundary: {declarations:#?}"
13855        );
13856    }
13857
13858    #[test]
13859    fn trailing_qualifiers_still_separate_genuine_overloads() {
13860        // The qualifier must keep distinguishing the real C++ overload sets it
13861        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
13862        let source = r#"
13863struct Widget {
13864  int* slot(int index);
13865  const int* slot(int index) const;
13866  int log(int severity) &;
13867  int log(int severity) &&;
13868};
13869"#;
13870        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
13871        assert_eq!(
13872            vec!["(int)".to_string(), "(int) const".to_string()],
13873            identity_signatures(&parsed, "Widget.slot")
13874        );
13875        assert_eq!(
13876            vec!["(int) &".to_string(), "(int) &&".to_string()],
13877            identity_signatures(&parsed, "Widget.log")
13878        );
13879    }
13880
13881    #[test]
13882    fn virtual_specifier_is_not_part_of_the_identity_signature() {
13883        // `override` never appears on the out-of-line definition, and C++ does
13884        // not make it part of the signature, so it must not split the identity.
13885        let source = r#"
13886struct Base {
13887  virtual void run(int value) const;
13888};
13889struct Widget : Base {
13890  void run(int value) const override;
13891};
13892void Widget::run(int value) const {}
13893"#;
13894        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
13895        assert_eq!(
13896            vec!["(int) const".to_string()],
13897            identity_signatures(&parsed, "Widget.run")
13898        );
13899    }
13900
13901    #[test]
13902    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
13903        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
13904        // the function type, so a declaration that spells `const int` and a
13905        // definition that spells `int` are one entity.
13906        let source = r#"
13907struct Widget {
13908  bool value_params(const int settings, const int supprs);
13909  void pointee_const(const int* p);
13910  void pointer_const(int* const p);
13911  void both_const(const int* const p);
13912  void reference_const(const int& p);
13913  void array_const(const int values[4]);
13914};
13915bool Widget::value_params(int settings, int supprs) { return true; }
13916void Widget::pointer_const(int* p) {}
13917void Widget::both_const(const int* p) {}
13918"#;
13919        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
13920        assert_eq!(
13921            vec!["(int, int)".to_string()],
13922            identity_signatures(&parsed, "Widget.value_params")
13923        );
13924        assert_eq!(
13925            vec!["(int *)".to_string()],
13926            identity_signatures(&parsed, "Widget.pointer_const")
13927        );
13928        assert_eq!(
13929            vec!["(const int *)".to_string()],
13930            identity_signatures(&parsed, "Widget.both_const")
13931        );
13932        // The const that is not top-level still distinguishes the type.
13933        assert_eq!(
13934            vec!["(const int *)".to_string()],
13935            identity_signatures(&parsed, "Widget.pointee_const")
13936        );
13937        assert_eq!(
13938            vec!["(const int &)".to_string()],
13939            identity_signatures(&parsed, "Widget.reference_const")
13940        );
13941        assert_eq!(
13942            vec!["(const int [4])".to_string()],
13943            identity_signatures(&parsed, "Widget.array_const")
13944        );
13945    }
13946
13947    #[test]
13948    fn top_level_parameter_const_still_separates_pointee_overloads() {
13949        let source = r#"
13950struct Widget {
13951  void take(const int* p);
13952  void take(int* p);
13953};
13954"#;
13955        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
13956        assert_eq!(
13957            vec!["(const int *)".to_string(), "(int *)".to_string()],
13958            identity_signatures(&parsed, "Widget.take")
13959        );
13960    }
13961
13962    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
13963        let mut parser = tree_sitter::Parser::new();
13964        parser
13965            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13966            .unwrap();
13967        let tree = parser.parse(source, None).unwrap();
13968        let start = source.find(callable_name).expect("callable declaration");
13969        let declarator =
13970            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
13971        cpp_comparable_parameter_shapes(declarator, source)
13972    }
13973
13974    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
13975        let mut shapes = comparable_shapes(source, callable_name);
13976        assert_eq!(1, shapes.len(), "{shapes:?}");
13977        match shapes.remove(0) {
13978            CppComparableSlot::Shape(shape) => shape,
13979            other => panic!("expected a comparable shape, got {other:?}"),
13980        }
13981    }
13982
13983    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
13984        let mut current = shape.root();
13985        loop {
13986            match shape.node(current) {
13987                CppComparableNode::Named { .. } => return shape.node(current),
13988                CppComparableNode::Pointer { inner, .. }
13989                | CppComparableNode::Reference { inner }
13990                | CppComparableNode::Array { inner } => current = *inner,
13991                CppComparableNode::Generic { base, .. } => current = *base,
13992            }
13993        }
13994    }
13995
13996    #[test]
13997    fn comparable_shape_keeps_pointee_const() {
13998        assert_ne!(
13999            sole_comparable_shape("void f(const char* p);", "f("),
14000            sole_comparable_shape("void f(char* p);", "f(")
14001        );
14002    }
14003
14004    #[test]
14005    fn comparable_shape_keeps_inner_pointer_const() {
14006        assert_ne!(
14007            sole_comparable_shape("void f(int** p);", "f("),
14008            sole_comparable_shape("void f(int* const* p);", "f(")
14009        );
14010    }
14011
14012    #[test]
14013    fn comparable_shape_drops_top_level_pointer_const() {
14014        assert_eq!(
14015            sole_comparable_shape("void f(int* const p);", "f("),
14016            sole_comparable_shape("void f(int* p);", "f(")
14017        );
14018    }
14019
14020    #[test]
14021    fn comparable_shape_drops_top_level_base_const() {
14022        assert_eq!(
14023            sole_comparable_shape("void f(const int p);", "f("),
14024            sole_comparable_shape("void f(int p);", "f(")
14025        );
14026    }
14027
14028    #[test]
14029    fn comparable_shape_decays_top_level_array_to_pointer() {
14030        assert_eq!(
14031            sole_comparable_shape("void f(int a[3]);", "f("),
14032            sole_comparable_shape("void f(int* a);", "f(")
14033        );
14034        assert_eq!(
14035            sole_comparable_shape("void f(int* a[3]);", "f("),
14036            sole_comparable_shape("void f(int** a);", "f(")
14037        );
14038    }
14039
14040    #[test]
14041    fn comparable_shape_keeps_array_behind_pointer() {
14042        assert_ne!(
14043            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
14044            sole_comparable_shape("struct S { void f(int** a); };", "f(")
14045        );
14046    }
14047
14048    #[test]
14049    fn comparable_shape_records_written_name_and_lexical_scope() {
14050        let declared =
14051            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
14052        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
14053        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
14054            panic!("named leaf");
14055        };
14056        assert_eq!(["Msg".to_string()].as_slice(), name.path());
14057        assert_eq!(
14058            ["ns".to_string(), "S".to_string()].as_slice(),
14059            name.lexical_scope()
14060        );
14061        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
14062            panic!("named leaf");
14063        };
14064        assert_eq!(
14065            ["ns".to_string(), "Msg".to_string()].as_slice(),
14066            name.path()
14067        );
14068        assert!(name.lexical_scope().is_empty());
14069        assert_ne!(declared, defined);
14070    }
14071
14072    #[test]
14073    fn comparable_shape_marks_sized_primitive_leaf() {
14074        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
14075        let CppComparableNode::Named {
14076            name, primitive, ..
14077        } = comparable_named_leaf(&shape)
14078        else {
14079            panic!("named leaf");
14080        };
14081        assert!(primitive);
14082        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
14083        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
14084    }
14085
14086    #[test]
14087    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
14088        assert_eq!(
14089            vec![CppComparableSlot::Unstructured],
14090            comparable_shapes("void f(void (*cb)(int));", "f(")
14091        );
14092    }
14093
14094    #[test]
14095    fn comparable_shape_reports_ellipsis_slot() {
14096        let shapes = comparable_shapes("void f(int a, ...);", "f(");
14097        assert_eq!(2, shapes.len(), "{shapes:?}");
14098        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
14099    }
14100
14101    #[test]
14102    fn comparable_shape_keeps_template_argument_const() {
14103        assert_ne!(
14104            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
14105            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
14106        );
14107    }
14108
14109    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
14110    /// file-scope tag that a later `struct inner *` at file scope may name.
14111    #[test]
14112    fn c_file_mints_aggregate_member_tag_at_file_scope() {
14113        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14114        let parsed = parse_cpp_declarations(source, "x.c");
14115        let declarations = parsed.declarations();
14116
14117        assert!(
14118            declarations
14119                .iter()
14120                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14121            "expected a file-scope inner tag, got {declarations:?}"
14122        );
14123        assert!(
14124            declarations
14125                .iter()
14126                .all(|unit| unit.fq_name() != "outer$inner"),
14127            "expected no nested identity, got {declarations:?}"
14128        );
14129        assert!(
14130            declarations
14131                .iter()
14132                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
14133        );
14134        // Members still belong to their own aggregate.
14135        assert!(
14136            declarations
14137                .iter()
14138                .any(|unit| unit.fq_name() == "inner.value")
14139        );
14140        assert!(
14141            declarations
14142                .iter()
14143                .any(|unit| unit.fq_name() == "outer.item")
14144        );
14145
14146        let outer = declarations
14147            .iter()
14148            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
14149            .expect("outer");
14150        assert!(
14151            parsed
14152                .children
14153                .get(outer)
14154                .into_iter()
14155                .flatten()
14156                .all(|child| child.fq_name() != "inner"),
14157            "the tag must not hang off the aggregate it is written inside: {:?}",
14158            parsed.children
14159        );
14160    }
14161
14162    /// A header carries no compilation language of its own, and a `.cpp`
14163    /// translation unit really does declare a nested class. Both keep exactly
14164    /// the C++ extraction they had before the C dialect existed.
14165    #[test]
14166    fn header_and_cpp_files_keep_nested_tag_identity() {
14167        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14168        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
14169            let parsed = parse_cpp_declarations(source, name);
14170            let declarations = parsed.declarations();
14171            assert!(
14172                declarations
14173                    .iter()
14174                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14175                "{name} must keep the nested identity, got {declarations:?}"
14176            );
14177            assert!(
14178                declarations.iter().all(|unit| unit.fq_name() != "inner"),
14179                "{name} must not mint a file-scope tag, got {declarations:?}"
14180            );
14181            assert!(
14182                declarations
14183                    .iter()
14184                    .any(|unit| unit.fq_name() == "outer$inner.value")
14185            );
14186        }
14187    }
14188
14189    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
14190    #[test]
14191    fn uppercase_c_extension_keeps_cpp_tag_scope() {
14192        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14193        let parsed = parse_cpp_declarations(source, "x.C");
14194        assert!(
14195            parsed
14196                .declarations()
14197                .iter()
14198                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
14199        );
14200    }
14201
14202    /// There is no such thing as a partially nested tag in C: every level of a
14203    /// nested aggregate chain lands at the same enclosing scope.
14204    #[test]
14205    fn c_file_mints_every_nesting_level_at_file_scope() {
14206        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
14207        let parsed = parse_cpp_declarations(source, "z.c");
14208        let declarations = parsed.declarations();
14209
14210        for tag in ["a", "b", "c"] {
14211            assert!(
14212                declarations
14213                    .iter()
14214                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
14215                "expected a file-scope {tag}, got {declarations:?}"
14216            );
14217        }
14218        assert!(
14219            declarations
14220                .iter()
14221                .all(|unit| !unit.fq_name().contains('$')),
14222            "no level may keep a nested identity, got {declarations:?}"
14223        );
14224        // Each member still belongs to the aggregate that declares it.
14225        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
14226        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
14227        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
14228    }
14229
14230    /// An enum tag is a tag; its enumerators stay members of the enum, which is
14231    /// what makes them ordinary identifiers at the enum's own (file) scope.
14232    #[test]
14233    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
14234        let source = "struct outer { enum color { RED, GREEN } c; };\n";
14235        let parsed = parse_cpp_declarations(source, "e.c");
14236        let declarations = parsed.declarations();
14237
14238        let color = declarations
14239            .iter()
14240            .find(|unit| unit.is_class() && unit.fq_name() == "color")
14241            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
14242        assert!(
14243            declarations
14244                .iter()
14245                .all(|unit| unit.fq_name() != "outer$color")
14246        );
14247        for enumerator in ["color.RED", "color.GREEN"] {
14248            assert!(
14249                declarations.iter().any(|unit| unit.fq_name() == enumerator),
14250                "expected {enumerator}, got {declarations:?}"
14251            );
14252        }
14253        let children = parsed
14254            .children
14255            .get(color)
14256            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
14257        assert!(
14258            ["color.RED", "color.GREEN"]
14259                .iter()
14260                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
14261            "enumerators must hang off their enum: {children:?}"
14262        );
14263    }
14264
14265    #[test]
14266    fn c_file_mints_member_list_union_at_file_scope() {
14267        let source = "struct outer { union inner { int a; float b; } item; };\n";
14268        let parsed = parse_cpp_declarations(source, "u.c");
14269        let declarations = parsed.declarations();
14270        assert!(
14271            declarations
14272                .iter()
14273                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14274            "expected a file-scope inner union, got {declarations:?}"
14275        );
14276        assert!(
14277            declarations
14278                .iter()
14279                .all(|unit| unit.fq_name() != "outer$inner")
14280        );
14281        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
14282        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
14283    }
14284
14285    /// A tag declared in a namespace member list is not a file-scope tag: the
14286    /// nearest enclosing non-aggregate scope is the namespace.
14287    #[test]
14288    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
14289        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
14290        let parsed = parse_cpp_declarations(source, "n.c");
14291        let declarations = parsed.declarations();
14292        let inner = declarations
14293            .iter()
14294            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
14295            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
14296        assert_eq!(inner.package_name(), "ns");
14297        assert!(
14298            declarations
14299                .iter()
14300                .all(|unit| unit.fq_name() != "ns.outer$inner")
14301        );
14302    }
14303
14304    /// Pins today's treatment of a tag declared inside a function body: the
14305    /// declaration walk does not descend into statement bodies, so no unit is
14306    /// minted for it in either dialect. C block scope is out of scope for the
14307    /// dialect change, and this test proves the change did not disturb it.
14308    #[test]
14309    fn function_local_tags_are_unchanged_in_both_dialects() {
14310        let source =
14311            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
14312        for name in ["y.c", "y.cpp"] {
14313            let parsed = parse_cpp_declarations(source, name);
14314            let declarations = parsed.declarations();
14315            assert!(
14316                declarations
14317                    .iter()
14318                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
14319                "{name}: {declarations:?}"
14320            );
14321            for tag in ["localtag", "deeper", "localtag$deeper"] {
14322                assert!(
14323                    declarations.iter().all(|unit| unit.fq_name() != tag),
14324                    "{name} must not mint {tag}, got {declarations:?}"
14325                );
14326            }
14327        }
14328    }
14329
14330    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
14331    /// re-scope: the typedef name is identical in both dialects.
14332    #[test]
14333    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
14334        let source = "typedef struct { int v; } T;\n";
14335        for name in ["t.c", "t.cpp"] {
14336            let parsed = parse_cpp_declarations(source, name);
14337            let declarations = parsed.declarations();
14338            assert!(
14339                declarations
14340                    .iter()
14341                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
14342                "{name}: {declarations:?}"
14343            );
14344        }
14345    }
14346
14347    /// `class` is not C. Source that spells one in a `.c` file is not C code,
14348    /// so it keeps the C++ reading rather than acquiring a half-C identity.
14349    #[test]
14350    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
14351        let source = "class outer { class inner { int v; }; };\n";
14352        let c_parsed = parse_cpp_declarations(source, "k.c");
14353        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
14354        let c_declarations = c_parsed.declarations();
14355        let cpp_declarations = cpp_parsed.declarations();
14356        assert!(
14357            c_declarations
14358                .iter()
14359                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14360            "{c_declarations:?}"
14361        );
14362        assert_eq!(
14363            c_declarations
14364                .iter()
14365                .map(|unit| unit.fq_name())
14366                .collect::<std::collections::BTreeSet<_>>(),
14367            cpp_declarations
14368                .iter()
14369                .map(|unit| unit.fq_name())
14370                .collect::<std::collections::BTreeSet<_>>()
14371        );
14372    }
14373}