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::{
9    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
10};
11use brokk_bifrost_core::analyzer::model::{
12    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
13    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
14    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
15    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
16    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
17};
18use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
19use brokk_bifrost_core::analyzer::structural::materialization::{
20    GenerationKind, MaterializationRecord,
21};
22use brokk_bifrost_core::analyzer::tree_walk::{ParentIndex, WalkControl, walk_named_tree_preorder};
23use brokk_bifrost_core::analyzer::{CodeUnit, ProjectFile};
24use brokk_bifrost_core::hash::{HashMap, HashSet};
25use regex::Regex;
26use tree_sitter::{Node, Parser, Tree};
27
28/// Intern one qualified-name segment in the process-global interner.
29fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
30    segment_interner().intern(text, kind)
31}
32
33/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
34/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
35/// `::` head is exactly the mixed-separator store issue #1163 is about; the
36/// structured form records each namespace component, and the equivalence check
37/// renders it natively (with `::` between adjacent Package segments) so it
38/// round-trips to the legacy string. Splitting the already-joined string here is
39/// the M1 bridge — the legacy strings stay authoritative until M3.
40fn cpp_push_package(fq: &mut FqName, package_name: &str) {
41    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
42        fq.push(cpp_segment(component, SegmentKind::Package));
43    }
44}
45
46/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
47const CPP_PACKAGE_SEPARATOR: &str = "::";
48
49/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
50/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
51/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
52/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
53/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
54/// cpp-specific native rendering rule is needed for this chain.
55fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
56    let mut first = true;
57    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
58    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
59    // entry point, not re-inference of an already-structured name.
60    for component in chain.split('$').filter(|c| !c.is_empty()) {
61        let kind = if first {
62            SegmentKind::Type
63        } else {
64            SegmentKind::Nested
65        };
66        fq.push(cpp_segment(component, kind));
67        first = false;
68    }
69}
70
71/// Structured name for a C++ namespace module: every `::`-separated component is
72/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
73/// `short_name` with an empty `package_name`).
74fn cpp_namespace_fq(full_name: &str) -> FqName {
75    let mut fq = FqName::new();
76    cpp_push_package(&mut fq, full_name);
77    fq
78}
79
80/// The per-level namespace components a `namespace_definition`'s `name` field
81/// declares.
82///
83/// A C++17 nested definition (`namespace a::b::c`) parses as a
84/// `nested_namespace_specifier` whose named children are the per-level
85/// `namespace_identifier`s plus, for three or more levels, a further
86/// `nested_namespace_specifier`; the `::` separators, the optional per-level
87/// `inline`, and the leading global `::` are all anonymous tokens the walk
88/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
89/// segment path as the expanded `namespace a { namespace b { } }` form.
90///
91/// A shape outside that grammar is the deliberately ill-formed source the
92/// diagnostic corpora carry. Those keep their historical single-component
93/// reading of the raw name text, which the caller still joins to the lexical
94/// namespace exactly as before.
95fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
96    let mut components = Vec::new();
97    let mut stack = vec![node];
98    while let Some(current) = stack.pop() {
99        match current.kind() {
100            "namespace_identifier" | "identifier" => {
101                components.push(normalize_cpp_whitespace(node_text(current, source)));
102            }
103            "nested_namespace_specifier" => {
104                for index in (0..current.named_child_count()).rev() {
105                    stack.push(
106                        current
107                            .named_child(index)
108                            .expect("index below the node's own named child count"),
109                    );
110                }
111            }
112            _ => return cpp_raw_namespace_name_components(node, source),
113        }
114    }
115    if components.iter().any(String::is_empty) {
116        return cpp_raw_namespace_name_components(node, source);
117    }
118    components
119}
120
121/// The historical reading of a namespace name node: its whole source text as
122/// one component, with a leading global `::` marker dropped so the caller's
123/// global-scope handling stays the AST boundary rather than a text prefix.
124///
125/// This is the recovery path for source outside the C++ grammar, so the text it
126/// returns can carry a separator that names nothing. react-native-windows
127/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
128/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
129/// trailing separator with no tail. The caller stores this component in
130/// `short_name` and derives the fq by splitting it back apart, so an empty tail
131/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
132/// here, at the one place the malformed text enters, rather than leaving each
133/// consumer to guard (#2353, a variant of #1878).
134fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
135    let start = node
136        .child(0)
137        .filter(|child| !child.is_named() && child.kind() == "::")
138        .map_or(node.start_byte(), |marker| marker.end_byte());
139    let text = normalize_cpp_whitespace(
140        source
141            .get(start..node.end_byte())
142            .expect("namespace name node covers one source range"),
143    );
144    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
145    if text.is_empty() {
146        return Vec::new();
147    }
148    vec![text]
149}
150
151/// Return the named namespace path that structurally encloses `node`.
152///
153/// This intentionally follows namespace AST ancestors rather than inspecting
154/// source text. Anonymous namespaces are not representable in the legacy C++
155/// package field, so a path containing one fails closed.
156fn cpp_lexical_namespace_name<'tree>(
157    node: Node<'tree>,
158    source: &str,
159    ancestry: &ParentIndex<'tree>,
160) -> Option<String> {
161    let mut components = Vec::new();
162    let mut ancestor = ancestry.parent(node);
163    while let Some(current) = ancestor {
164        if current.kind() == "namespace_definition" {
165            let name_node = current.child_by_field_name("name")?;
166            let name = normalize_cpp_whitespace(node_text(name_node, source));
167            if name.is_empty() {
168                return None;
169            }
170            components.push(name);
171        }
172        ancestor = ancestry.parent(current);
173    }
174    if components.is_empty() {
175        return None;
176    }
177    components.reverse();
178    // Same shared empty-component decision as `cpp_push_package`, which splits
179    // this string back apart: an enclosing namespace whose own name node is
180    // malformed contributes a component carrying its own separator (#2353).
181    Some(
182        normalize_joined(
183            &components.join(CPP_PACKAGE_SEPARATOR),
184            CPP_PACKAGE_SEPARATOR,
185        )
186        .into_owned(),
187    )
188}
189
190/// Nested-class `$` join for short names. An anonymous parent class (empty
191/// short_name) contributes no segment: the FqName bridge drops empty
192/// components, so a bare `parent$child` join would desync `short_name` from
193/// the fq and trip the package/short boundary assert in
194/// `CodeUnit::with_signature_and_fq` (#2140).
195fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
196    if parent_short.is_empty() {
197        name.to_string()
198    } else {
199        format!("{parent_short}${name}")
200    }
201}
202
203/// Member `.` join for short names; same anonymous-parent guard as
204/// [`cpp_join_nested_short`] (#2140).
205fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
206    if parent_short.is_empty() {
207        name.to_string()
208    } else {
209        format!("{parent_short}.{name}")
210    }
211}
212
213/// Structural fq for a leaf declaration: the parent unit's fq plus this
214/// declaration's own name as one segment (or the package segments plus the
215/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
216/// chain, so a literal `$` inside a source identifier (Cython template
217/// substitution points, gcc `$`-identifiers) survives instead of corrupting
218/// the chain and tripping the package/short boundary assert (#2140).
219fn cpp_leaf_fq(
220    package_name: &str,
221    parent: Option<&CodeUnit>,
222    name: &str,
223    kind_if_nested: SegmentKind,
224    kind_if_top: SegmentKind,
225) -> FqName {
226    if let Some(parent) = parent {
227        parent
228            .fq()
229            .clone()
230            .with_pushed(cpp_segment(name, kind_if_nested))
231    } else {
232        let mut fq = FqName::new();
233        cpp_push_package(&mut fq, package_name);
234        fq.push(cpp_segment(name, kind_if_top));
235        fq
236    }
237}
238
239/// Structured name for a member unit (function, field, enumerator). The
240/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
241/// the member has an owner, by `.member`; free functions and globals have no
242/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
243/// C++ member names never contain a literal `.`, so the single `.` (if any)
244/// separates the owner chain from the member.
245pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
246    let mut fq = FqName::new();
247    cpp_push_package(&mut fq, package_name);
248    match short_name.rsplit_once('.') {
249        Some((owner_chain, member)) => {
250            cpp_push_type_chain(&mut fq, owner_chain);
251            fq.push(cpp_segment(member, SegmentKind::Member));
252        }
253        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
254    }
255    fq
256}
257
258#[derive(Clone)]
259pub struct ScopeInfo {
260    package_name: String,
261    module: Option<CodeUnit>,
262    class_unit: Option<CodeUnit>,
263    template_signature: Option<String>,
264    template_metadata: Option<CppTemplateMetadata>,
265    declarations_are_fields: bool,
266    recovered_specialization_member_scope: bool,
267    /// Namespace targets of every `using namespace X;` directive lexically
268    /// visible at this point in the file (declaration order), threaded
269    /// forward sibling-by-sibling by the sequential container walk (see
270    /// `CppWork::Siblings`). An out-of-line member definition written as a
271    /// bare `Class::method` at file/namespace scope with no enclosing
272    /// `namespace {}` block (issue #1093, e.g. log4cxx's
273    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
274    /// const { ... }`) has no other structural signal for which namespace
275    /// actually owns `Class`; this is the best-effort candidate list used to
276    /// recover it so the definition's indexed identity matches its header
277    /// declaration's.
278    visible_using_namespaces: Vec<String>,
279}
280
281struct CppContainer<'tree> {
282    node: Node<'tree>,
283    scope: ScopeInfo,
284}
285
286struct CppNodeWork<'tree> {
287    node: Node<'tree>,
288    scope: ScopeInfo,
289}
290
291/// Cursor over one container's remaining named children, processed one at a
292/// time (rather than all at once) so a `using namespace X;` sibling can
293/// update `scope.visible_using_namespaces` for the siblings that follow it,
294/// matching real C++ using-directive semantics. Nested container work is
295/// still pushed and fully drained before the cursor resumes (stack LIFO
296/// order), preserving the original left-to-right visitation order.
297struct CppSiblingsWork<'tree> {
298    children: std::vec::IntoIter<Node<'tree>>,
299    scope: ScopeInfo,
300}
301
302enum CppWork<'tree> {
303    Container(CppContainer<'tree>),
304    Node(CppNodeWork<'tree>),
305    Siblings(CppSiblingsWork<'tree>),
306}
307
308fn class_like_name<'tree>(
309    node: Node<'tree>,
310    source: &str,
311    ancestry: &ParentIndex<'tree>,
312) -> Option<String> {
313    let best = class_like_name_from_children(node, source);
314    if let Some(parent) = ancestry.parent(node)
315        && matches!(
316            parent.kind(),
317            "declaration" | "field_declaration" | "function_definition"
318        )
319        // A class_specifier carrying its own body proves the grammar name is
320        // the real class name: a sibling declarator then declares an object
321        // (`class X {} x;`), never a displaced class name. The gate matters
322        // when the class name is itself an all-caps token (`X`, `API`) --
323        // without it the export-macro re-read below steals the object
324        // declarator's name for the class (#2283). The genuine export-macro
325        // shapes leave the class_specifier bodyless, the same invariant
326        // recover_malformed_exported_multiple_base_class already gates on.
327        && cpp_body_node(node).is_none()
328        && node
329            .child_by_field_name("name")
330            .map(|name_node| {
331                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
332            })
333            .unwrap_or(false)
334        && let Some(recovered) = exported_class_name_from_node(parent, source)
335        && best.as_deref() != Some(recovered.as_str())
336    {
337        return Some(recovered);
338    }
339    best.or_else(|| {
340        node.child_by_field_name("name")
341            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
342            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
343    })
344}
345
346fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
347    let mut grammar_name = None;
348    if let Some(name_node) = node.child_by_field_name("name") {
349        let name = normalize_cpp_whitespace(node_text(name_node, source));
350        if name.is_empty() {
351            return None;
352        }
353        if !cpp_export_macro_token(&name) {
354            return Some(name);
355        }
356        grammar_name = Some(name);
357    }
358
359    let mut best = None;
360    let mut cursor = node.walk();
361    let mut stack = Vec::new();
362    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
363        if matches!(
364            child.kind(),
365            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
366        ) {
367            break;
368        }
369        stack.push(child);
370    }
371
372    while let Some(current) = stack.pop() {
373        if matches!(current.kind(), "type_identifier" | "identifier") {
374            let name = normalize_cpp_whitespace(node_text(current, source));
375            if !name.is_empty() && !cpp_export_macro_token(&name) {
376                best = Some(name);
377            }
378            continue;
379        }
380
381        for index in (0..current.named_child_count()).rev() {
382            if let Some(child) = current.named_child(index) {
383                stack.push(child);
384            }
385        }
386    }
387    best.or(grammar_name)
388}
389
390pub fn cpp_export_macro_token(token: &str) -> bool {
391    token
392        .chars()
393        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
394}
395
396struct RecoveredExportedClass<'tree> {
397    declaration_node: Node<'tree>,
398    name: String,
399    body: Option<Node<'tree>>,
400    raw_supertypes: Option<Vec<String>>,
401    uses_initializer_body: bool,
402    /// Present only for the fragmented multiple-base export shape (issue #938).
403    /// Carries the true class-body byte region -- the members tree-sitter scattered
404    /// out of the recovered node -- so they can be reparsed and re-owned as members
405    /// rather than lost inside the truncated `initializer_list` stand-in.
406    fragmented_body: Option<FragmentedExportBody>,
407}
408
409/// The recovered class-body geometry for a fragmented multiple-base export class.
410/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
411/// verbatim for a region reparse (issue #941 machinery) so every recovered member
412/// keeps its exact original byte/line position. `class_range` is the full class
413/// navigation range spanning to the displaced closing brace.
414struct FragmentedExportBody {
415    reparse_start: usize,
416    reparse_end: usize,
417    class_range: Range,
418}
419
420struct DisplacedFragmentNamespaceBoundary<'tree> {
421    class_close: Node<'tree>,
422    class_semicolon: Node<'tree>,
423    namespace_items: Vec<Node<'tree>>,
424}
425
426/// Result of validating a reparsed fragmented class body.  A complete tree can
427/// safely consume the whole region.  A partial tree may contain only the exact
428/// class-named constructor that tree-sitter merged into an access label; its
429/// remaining siblings must stay on the ordinary outer walk.
430enum FragmentedExportMembers {
431    Complete(Tree),
432    ConditionalConstructor(Tree),
433}
434
435#[derive(Clone, Copy)]
436struct DisplacedMacroClassTail {
437    split_index: usize,
438    class_range: Range,
439}
440
441fn recover_exported_class_declaration<'tree>(
442    node: Node<'tree>,
443    source: &str,
444) -> Option<RecoveredExportedClass<'tree>> {
445    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
446        return Some(recovered);
447    }
448
449    let class_node = first_class_like_child(node)?;
450    if let Some(name_node) = class_node.child_by_field_name("name") {
451        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
452        if cpp_export_macro_token(&class_name) {
453            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
454            // Name declarator. Only a bare declarator can be the displaced class name;
455            // wrappers describe an object whose type merely happens to look macro-like.
456            let mut cursor = node.walk();
457            if node
458                .children_by_field_name("declarator", &mut cursor)
459                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
460            {
461                return None;
462            }
463        } else if has_direct_cpp_declarator(node) {
464            return None;
465        }
466    }
467    let name = exported_class_name_from_node(class_node, source)?;
468    Some(RecoveredExportedClass {
469        declaration_node: class_node,
470        name,
471        body: cpp_body_node(class_node),
472        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
473            .then(|| extract_cpp_supertypes(class_node, source)),
474        uses_initializer_body: false,
475        fragmented_body: None,
476    })
477}
478
479fn recover_malformed_exported_base_class<'tree>(
480    node: Node<'tree>,
481    source: &str,
482) -> Option<RecoveredExportedClass<'tree>> {
483    if node.kind() != "declaration" {
484        return None;
485    }
486    let class_node = node.child_by_field_name("type")?;
487    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
488        return None;
489    }
490    let macro_name = class_node
491        .child_by_field_name("name")
492        .and_then(|name| direct_identifier_name(name, source))?;
493    if !cpp_export_macro_token(&macro_name) {
494        return None;
495    }
496
497    let mut named_cursor = node.walk();
498    let mut named = node.named_children(&mut named_cursor);
499    if named
500        .next()
501        .is_none_or(|child| !same_node(child, class_node))
502    {
503        return None;
504    }
505    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
506    if displaced.kind() != "ERROR" {
507        return None;
508    }
509    let name = displaced_exported_class_name(displaced, source)?;
510
511    let remaining = named.collect::<Vec<_>>();
512    let init = *remaining.last()?;
513    if init.kind() != "init_declarator" {
514        return None;
515    }
516    let final_base = init
517        .child_by_field_name("declarator")
518        .and_then(|base| recovered_malformed_base_name(base, source))?;
519    let body = init.child_by_field_name("value")?;
520    // A complete reduction has a real closing brace here. In Chromium's Widget
521    // declaration, tree-sitter instead emits the same direct `}` slot as a
522    // zero-width missing node where the first body macro truncates the prefix.
523    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
524        return None;
525    }
526
527    if remaining[..remaining.len() - 1]
528        .iter()
529        .any(|child| match child.kind() {
530            "qualified_identifier"
531            | "scoped_type_identifier"
532            | "type_identifier"
533            | "identifier" => false,
534            "ERROR" => !is_malformed_inheritance_access(*child, source),
535            _ => true,
536        })
537    {
538        return None;
539    }
540
541    let mut raw_supertypes = Vec::new();
542    for base in &remaining[..remaining.len() - 1] {
543        if base.kind() == "ERROR" {
544            continue;
545        }
546        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
547    }
548    raw_supertypes.push(final_base);
549
550    Some(RecoveredExportedClass {
551        declaration_node: node,
552        name,
553        body: Some(body),
554        raw_supertypes: Some(raw_supertypes),
555        uses_initializer_body: true,
556        fragmented_body: fragmented_export_body_region(node, body, source),
557    })
558}
559
560/// Locate the true class-body region for a fragmented multiple-base export class.
561///
562/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
563/// emits in place of the real class body. Tree-sitter reduces that body in one of
564/// two shapes, both of which lose the members from the recovered node:
565///
566/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
567///   a real closing brace and holds the whole body text inline. The interior between
568///   the braces reparses to the members directly.
569/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
570///   first member with a zero-width MISSING `}`; every later member -- and the real
571///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
572///   siblings. The interior runs from the opening brace to that displaced `}`.
573///
574/// Returns the interior byte range to reparse plus the full class navigation range.
575fn fragmented_export_body_region(
576    node: Node<'_>,
577    body: Node<'_>,
578    source: &str,
579) -> Option<FragmentedExportBody> {
580    let reparse_start = body.start_byte() + 1;
581    let close = direct_close_brace(body)?;
582    if close.end_byte() > close.start_byte() {
583        return Some(FragmentedExportBody {
584            reparse_start,
585            reparse_end: close.start_byte(),
586            class_range: cpp_declaration_range(node),
587        });
588    }
589    // The closing brace was displaced past the recovered node. A balanced nested
590    // class keeps its own braces, so the first lone-`}` sibling is this class's.
591    let mut sibling = node.next_named_sibling();
592    let displaced_close = loop {
593        let Some(current) = sibling else {
594            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
595        };
596        if cpp_is_stray_close_brace(current, source) {
597            break current;
598        }
599        sibling = current.next_named_sibling();
600    };
601    Some(FragmentedExportBody {
602        reparse_start,
603        reparse_end: displaced_close.start_byte(),
604        class_range: Range {
605            start_byte: node.start_byte(),
606            end_byte: displaced_close.end_byte(),
607            start_line: node.start_position().row + 1,
608            end_line: displaced_close.end_position().row + 1,
609        },
610    })
611}
612
613/// Locate the true class-body region for the export-macro class shape that
614/// tree-sitter promotes to a `function_definition`.
615///
616/// In this shape the synthetic function body closes at the first inline
617/// method, while the class's real members continue as root-level siblings until
618/// a stray `}` followed by the displaced class `;`. Reparse the complete
619/// interior so those siblings are visited with the recovered class scope.
620fn fragmented_export_function_body_region(
621    node: Node<'_>,
622    body: Node<'_>,
623    source: &str,
624    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
625) -> Option<FragmentedExportBody> {
626    let reparse_start = body.start_byte().checked_add(1)?;
627    if let Some(boundary) = displaced_namespace {
628        return Some(FragmentedExportBody {
629            reparse_start,
630            reparse_end: boundary.class_close.start_byte(),
631            class_range: Range {
632                start_byte: node.start_byte(),
633                end_byte: boundary.class_semicolon.end_byte(),
634                start_line: node.start_position().row + 1,
635                end_line: boundary.class_semicolon.end_position().row + 1,
636            },
637        });
638    }
639    let siblings = cpp_following_named_siblings(node, source);
640    let boundary = fragmented_export_sibling_class_boundary(node, source);
641    let boundary_index = boundary.and_then(|boundary| {
642        siblings
643            .iter()
644            .position(|candidate| same_node(*candidate, boundary))
645    });
646    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
647    let mut sibling_index = 0;
648    // A complete recovered class's synthetic wrapper is immediately followed
649    // by its displaced semicolon (comments and a trailing attribute macro --
650    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
651    // may sit between the body and that semicolon). Only scan for a later
652    // stray close when real member siblings intervene; otherwise every earlier
653    // complete class would borrow the next malformed class's close and claim
654    // its members. The trailing-attribute case is the gtest shape: the scan
655    // borrowed a close ~1900 lines later and re-owned a following
656    // `namespace testing { namespace internal {` block as class members,
657    // doubling the package path ("testing::internal::testing::internal") and
658    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
659    // boundary assert (#2297).
660    while let Some(current) = siblings.get(sibling_index).copied() {
661        if current.kind() == "comment" {
662            sibling_index += 1;
663            continue;
664        }
665        if is_trailing_attribute_macro_sibling(current) {
666            sibling_index += 1;
667            continue;
668        }
669        if cpp_is_stray_semicolon(current, source) {
670            return None;
671        }
672        break;
673    }
674    while let Some(current) = siblings.get(sibling_index).copied() {
675        let next = siblings.get(sibling_index + 1).copied();
676        if cpp_is_stray_close_brace(current, source)
677            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
678        {
679            let semicolon = next.expect("checked above");
680            return Some(FragmentedExportBody {
681                reparse_start,
682                reparse_end: current.start_byte(),
683                class_range: Range {
684                    start_byte: node.start_byte(),
685                    end_byte: semicolon.end_byte(),
686                    start_line: node.start_position().row + 1,
687                    end_line: semicolon.end_position().row + 1,
688                },
689            });
690        }
691        // When the final access label keeps the class close in its malformed
692        // declaration body, tree-sitter nests the lone `}` ERROR below the
693        // label instead of exposing it as a direct sibling. Search only the
694        // scattered siblings after the synthetic wrapper. The first such
695        // close is the class terminator because nested class bodies retain
696        // their own balanced class_specifier nodes.
697        if current.start_byte() >= body.end_byte()
698            && let Some(close) = cpp_nested_stray_close_brace(current, source)
699        {
700            return Some(FragmentedExportBody {
701                reparse_start,
702                reparse_end: close.start_byte(),
703                class_range: Range {
704                    start_byte: node.start_byte(),
705                    end_byte: current.end_byte(),
706                    start_line: node.start_position().row + 1,
707                    end_line: current.end_position().row + 1,
708                },
709            });
710        }
711        sibling_index += 1;
712    }
713    boundary.map(|boundary| FragmentedExportBody {
714        reparse_start,
715        reparse_end: boundary.start_byte(),
716        class_range: Range {
717            start_byte: node.start_byte(),
718            end_byte: boundary.start_byte(),
719            start_line: node.start_position().row + 1,
720            end_line: boundary.start_position().row + 1,
721        },
722    })
723}
724
725/// Find a later macro-export class that tree-sitter lifted through an enclosing
726/// preprocessor container. A class that is still a direct sibling can be a
727/// nested member of the current fragmented class, so only a changed parent is
728/// a proven boundary between the two recovered class envelopes.
729fn fragmented_export_sibling_class_boundary<'tree>(
730    node: Node<'tree>,
731    source: &str,
732) -> Option<Node<'tree>> {
733    let node_parent = node.parent()?;
734    cpp_following_named_siblings(node, source)
735        .into_iter()
736        .find(|candidate| {
737            recover_exported_class_function_definition(*candidate, source).is_some()
738                && candidate
739                    .parent()
740                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
741        })
742}
743
744/// A trailing attribute macro after a recovered class's closing brace, spelled
745/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
746/// bare identifier is never a class member (members need a type), so this
747/// sibling can only be the class's own tail (#2297).
748fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
749    if node.kind() != "expression_statement" {
750        return false;
751    }
752    let mut cursor = node.walk();
753    let mut children = node.named_children(&mut cursor);
754    children
755        .next()
756        .is_some_and(|child| child.kind() == "identifier")
757        && children.next().is_none()
758}
759
760/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
761/// export-class wrapper can place the class close inside an access-label node,
762/// so direct-sibling checks alone miss the boundary.  Walk named CST children
763/// only; the helper does not inspect source text beyond the existing structured
764/// stray-brace predicate.
765fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
766    let mut stack = vec![node];
767    while let Some(current) = stack.pop() {
768        if cpp_is_stray_close_brace(current, source) {
769            return Some(current);
770        }
771        let mut cursor = current.walk();
772        stack.extend(current.named_children(&mut cursor));
773    }
774    None
775}
776
777/// Return named siblings that follow `node`, including siblings that tree-sitter
778/// attached to an enclosing container after malformed recovery split the local
779/// declaration list. Stop at the first structurally visible class close so a
780/// later namespace or exported class cannot supply the recovery boundary.
781fn cpp_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
782    let mut siblings = Vec::new();
783    let mut anchor = node;
784    while let Some(parent) = anchor.parent() {
785        let at_translation_unit = parent.kind() == "translation_unit";
786        let mut sibling = anchor.next_named_sibling();
787        while let Some(current) = sibling {
788            if at_translation_unit
789                && (current.kind() == "namespace_definition"
790                    || (current.kind() == "function_definition"
791                        && first_class_like_child(current).is_some()))
792            {
793                return siblings;
794            }
795            siblings.push(current);
796            if cpp_is_stray_close_brace(current, source) {
797                if let Some(semicolon) = current
798                    .next_named_sibling()
799                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
800                {
801                    siblings.push(semicolon);
802                }
803                return siblings;
804            }
805            if current.start_byte() >= node.end_byte()
806                && matches!(current.kind(), "ERROR" | "labeled_statement")
807                && cpp_nested_stray_close_brace(current, source).is_some()
808            {
809                return siblings;
810            }
811            sibling = current.next_named_sibling();
812        }
813        anchor = parent;
814    }
815    siblings
816}
817
818fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
819    if node.start_byte() >= class_end {
820        return false;
821    }
822    node.end_byte() <= class_end
823        || cpp_nested_stray_close_brace(node, source)
824            .is_some_and(|close| close.start_byte() == class_end)
825}
826
827/// Recover a plain class whose opening prefix is retained in one ERROR node
828/// while one or more nested class closes and the outer close are displaced to
829/// sibling `}`/`;` nodes. This is the non-export counterpart to the fragmented
830/// export-class recovery above. All boundaries come from tree-sitter nodes: the
831/// direct class tokens establish nesting depth and the displaced close nodes
832/// terminate it.
833fn fragmented_plain_class_body<'tree>(
834    node: Node<'tree>,
835    source: &str,
836) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
837    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
838        return Some(recovered);
839    }
840    if node.kind() != "ERROR" {
841        return None;
842    }
843    let mut cursor = node.walk();
844    let children = node.children(&mut cursor).collect::<Vec<_>>();
845    let keyword = children.first()?;
846    if !matches!(keyword.kind(), "class" | "struct" | "union") {
847        return None;
848    }
849    let name_node = children
850        .iter()
851        .copied()
852        .skip(1)
853        .find(|child| child.is_named())?;
854    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
855        return None;
856    }
857    let name = normalize_cpp_whitespace(node_text(name_node, source));
858    if name.is_empty() || cpp_export_macro_token(&name) {
859        return None;
860    }
861    let open_index = children.iter().position(|child| child.kind() == "{")?;
862    let open = children[open_index];
863    let nested_class_opens = children[open_index + 1..]
864        .iter()
865        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
866        .count();
867    let mut closes_remaining = 1 + nested_class_opens;
868    let mut sibling = node.next_named_sibling();
869    while let Some(candidate) = sibling {
870        let next = candidate.next_named_sibling();
871        if cpp_is_stray_close_brace(candidate, source) {
872            closes_remaining -= 1;
873            if closes_remaining == 0 {
874                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
875                if open.end_byte() >= candidate.start_byte() {
876                    return None;
877                }
878                return Some((
879                    node,
880                    name,
881                    FragmentedExportBody {
882                        reparse_start: open.end_byte(),
883                        reparse_end: candidate.start_byte(),
884                        class_range: Range {
885                            start_byte: node.start_byte(),
886                            end_byte: semicolon.end_byte(),
887                            start_line: node.start_position().row + 1,
888                            end_line: semicolon.end_position().row + 1,
889                        },
890                    },
891                ));
892            }
893        }
894        sibling = next;
895    }
896    None
897}
898
899pub(crate) fn recovered_fragmented_plain_class_has_body(
900    node: Node<'_>,
901    source: &str,
902    expected_name: &str,
903    expected_range: &Range,
904) -> bool {
905    fragmented_plain_class_body(node, source).is_some_and(|(_, name, fragmented)| {
906        name == expected_name
907            && fragmented.class_range.start_byte == expected_range.start_byte
908            && fragmented.class_range.end_byte == expected_range.end_byte
909    })
910}
911
912/// Recover a plain class whose parser-visible body ends inside a malformed
913/// inline member. Tree-sitter then attaches either the next real member
914/// declarator or the unfinished `else` branch directly to the outer function
915/// definition and leaves the class's actual `};` among later siblings. Those
916/// structured continuations and the close/semicolon siblings establish the
917/// complete body envelope without interpreting source text.
918fn fragmented_plain_class_declaration_body<'tree>(
919    node: Node<'tree>,
920    source: &str,
921) -> Option<(Node<'tree>, String, FragmentedExportBody)> {
922    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
923        return None;
924    }
925    let class_node = node.child_by_field_name("type")?;
926    if !matches!(
927        class_node.kind(),
928        "class_specifier" | "struct_specifier" | "union_specifier"
929    ) {
930        return None;
931    }
932    let name_node = class_node.child_by_field_name("name")?;
933    let name = normalize_cpp_whitespace(node_text(name_node, source));
934    if name.is_empty() || cpp_export_macro_token(&name) {
935        return None;
936    }
937    let body = cpp_body_node(class_node)?;
938    if body.kind() != "field_declaration_list" {
939        return None;
940    }
941    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
942        if declarator.start_byte() < class_node.end_byte() {
943            return None;
944        }
945        let mut cursor = node.walk();
946        node.named_children(&mut cursor).any(|child| {
947            if child.kind() != "ERROR"
948                || child.start_byte() < class_node.end_byte()
949                || child.end_byte() > declarator.start_byte()
950            {
951                return false;
952            }
953            let mut cursor = child.walk();
954            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
955            let Some((return_type, attributes)) = components.split_last() else {
956                return false;
957            };
958            matches!(
959                return_type.kind(),
960                "identifier"
961                    | "type_identifier"
962                    | "primitive_type"
963                    | "decltype"
964                    | "placeholder_type_specifier"
965            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
966                && attributes.iter().all(|attribute| {
967                    matches!(attribute.kind(), "identifier" | "type_identifier")
968                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
969                            *attribute, source,
970                        )))
971                })
972        })
973    } else {
974        let mut cursor = node.walk();
975        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
976        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
977            if same_node(*candidate_class, class_node)
978                && continuation.kind() == "identifier"
979                && node_text(*continuation, source) == "else"
980                && continuation_body.kind() == "compound_statement"
981                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
982                && continuation_body
983                    .child(continuation_body.child_count().saturating_sub(1))
984                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
985    };
986    if !displaced_member {
987        return None;
988    }
989    let open = body
990        .children(&mut body.walk())
991        .find(|child| child.kind() == "{")?;
992    let siblings = cpp_following_named_siblings(node, source);
993    let ordinary_boundary =
994        siblings
995            .iter()
996            .copied()
997            .enumerate()
998            .find_map(|(close_index, close)| {
999                cpp_is_stray_close_brace(close, source)
1000                    .then(|| {
1001                        siblings
1002                            .get(close_index + 1)
1003                            .copied()
1004                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1005                            .map(|semicolon| (close, semicolon))
1006                    })
1007                    .flatten()
1008            });
1009    let (close, semicolon) =
1010        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1011            (boundary.class_close, boundary.class_semicolon)
1012        } else {
1013            ordinary_boundary?
1014        };
1015    if open.end_byte() >= close.start_byte() {
1016        return None;
1017    }
1018    Some((
1019        class_node,
1020        name,
1021        FragmentedExportBody {
1022            reparse_start: open.end_byte(),
1023            reparse_end: close.start_byte(),
1024            class_range: Range {
1025                start_byte: class_node.start_byte(),
1026                end_byte: semicolon.end_byte(),
1027                start_line: class_node.start_position().row + 1,
1028                end_line: semicolon.end_position().row + 1,
1029            },
1030        },
1031    ))
1032}
1033
1034fn displaced_export_function_namespace_shape<'tree>(
1035    declaration: Node<'tree>,
1036    source: &str,
1037) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1038    let mut nested = Vec::new();
1039    for index in (0..declaration.named_child_count()).rev() {
1040        nested.push(declaration.named_child(index)?);
1041    }
1042    while let Some(current) = nested.pop() {
1043        // A recovered export class nested in this class can consume the first
1044        // parser-visible namespace close itself. In that shape the existing
1045        // later-class boundary logic already distinguishes the nested and
1046        // namespace-sibling owners; do not mistake the nested close for this
1047        // class's terminator.
1048        if recover_exported_class_function_definition(current, source).is_some() {
1049            return None;
1050        }
1051        for index in (0..current.named_child_count()).rev() {
1052            nested.push(current.named_child(index)?);
1053        }
1054    }
1055    let mut same_envelope_sibling = declaration.next_named_sibling();
1056    while let Some(current) = same_envelope_sibling {
1057        if recover_exported_class_function_definition(current, source).is_some() {
1058            return None;
1059        }
1060        same_envelope_sibling = current.next_named_sibling();
1061    }
1062    let declaration_list = declaration.parent()?;
1063    if declaration_list.kind() != "declaration_list" {
1064        return None;
1065    }
1066    let namespace = declaration_list.parent()?;
1067    if namespace.kind() != "namespace_definition"
1068        || namespace.child_by_field_name("body") != Some(declaration_list)
1069    {
1070        return None;
1071    }
1072    let class_close = direct_close_brace(declaration_list)?;
1073    let trailing_semicolon = namespace.next_named_sibling()?;
1074    if trailing_semicolon.kind() != "expression_statement"
1075        || trailing_semicolon.named_child_count() != 0
1076    {
1077        return None;
1078    }
1079    // A chain of malformed export classes can consume one parser-visible
1080    // namespace close per class. Walk through the enclosing sibling levels so
1081    // the later real namespace close remains the structural boundary; a
1082    // direct next-sibling walk stops at the first collapsed namespace and
1083    // incorrectly makes its intervening items members of this class.
1084    let siblings = cpp_following_named_siblings(namespace, source);
1085    let trailing_index = siblings
1086        .iter()
1087        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1088    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1089        recover_exported_class_function_definition(*candidate, source).is_some()
1090    }) {
1091        // Consecutive recovered classes already have an exact sibling-class
1092        // boundary. Preserve that established path, including nested export
1093        // classes, instead of interpreting the first class close as a
1094        // collapsed namespace boundary.
1095        return None;
1096    }
1097    let mut namespace_items = Vec::new();
1098    let mut nested_fragment_end = 0;
1099    for current in siblings.into_iter().skip(trailing_index + 1) {
1100        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1101        {
1102            return Some(DisplacedFragmentNamespaceBoundary {
1103                class_close,
1104                class_semicolon: trailing_semicolon,
1105                namespace_items,
1106            });
1107        }
1108        if current.start_byte() >= nested_fragment_end
1109            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1110        {
1111            nested_fragment_end = fragmented.class_range.end_byte;
1112        } else if current.start_byte() >= nested_fragment_end
1113            && recover_exported_class_function_definition(current, source).is_some()
1114            && let Some(body) = cpp_body_node(current)
1115            && let Some(fragmented) =
1116                fragmented_export_function_body_region(current, body, source, None)
1117        {
1118            nested_fragment_end = fragmented.class_range.end_byte;
1119        }
1120        namespace_items.push(current);
1121    }
1122    None
1123}
1124
1125fn displaced_fragment_namespace_boundary<'tree>(
1126    declaration: Node<'tree>,
1127    body: Node<'tree>,
1128    source: &str,
1129) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1130    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1131    let reparse_start = body.start_byte() + 1;
1132    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1133    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1134}
1135
1136/// Recover the class/namespace brace geometry for a declaration whose class
1137/// close tree-sitter consumed as the enclosing namespace close. This proof is
1138/// independent of whether every member in the class body can be reparsed: the
1139/// ordinary-tree fallback can still re-own bounded sibling declarations when
1140/// an unknown macro makes the complete body reparse unsafe.
1141fn displaced_fragment_namespace_geometry<'tree>(
1142    declaration: Node<'tree>,
1143    source: &str,
1144) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1145    // A templated class's malformed function wrapper remains beneath the
1146    // template node even though its later members have escaped to the
1147    // enclosing declaration list. Lift only that exact declaration child.
1148    let envelope = declaration
1149        .parent()
1150        .filter(|parent| {
1151            parent.kind() == "template_declaration"
1152                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1153        })
1154        .unwrap_or(declaration);
1155    let declaration_list = envelope.parent()?;
1156    if declaration_list.kind() != "declaration_list" {
1157        return None;
1158    }
1159    let namespace = declaration_list.parent()?;
1160    if namespace.kind() != "namespace_definition"
1161        || namespace.child_by_field_name("body") != Some(declaration_list)
1162    {
1163        return None;
1164    }
1165    let class_close = direct_close_brace(declaration_list)?;
1166    let trailing_semicolon = namespace.next_named_sibling()?;
1167    if trailing_semicolon.kind() != "expression_statement"
1168        || trailing_semicolon.named_child_count() != 0
1169    {
1170        return None;
1171    }
1172    let mut namespace_items = Vec::new();
1173    let mut sibling = trailing_semicolon.next_named_sibling();
1174    let mut nested_fragment_end = 0;
1175    loop {
1176        let current = sibling?;
1177        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1178        {
1179            break;
1180        }
1181        if current.start_byte() >= nested_fragment_end
1182            && let Some((_, _, fragmented)) = fragmented_plain_class_body(current, source)
1183        {
1184            nested_fragment_end = fragmented.class_range.end_byte;
1185        }
1186        namespace_items.push(current);
1187        sibling = current.next_named_sibling();
1188    }
1189    Some(DisplacedFragmentNamespaceBoundary {
1190        class_close,
1191        class_semicolon: trailing_semicolon,
1192        namespace_items,
1193    })
1194}
1195
1196/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1197fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1198    (0..node.child_count())
1199        .filter_map(|index| node.child(index))
1200        .find(|child| !child.is_named() && child.kind() == "}")
1201}
1202
1203/// A displaced lone closing brace: the class close that the fragmented multiple-base
1204/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1205fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1206    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1207}
1208
1209/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1210/// text while skipping line/block comments and string/char literals. The
1211/// exported-class recovery needs this when tree-sitter's bogus
1212/// `function_definition` body runs past the class's true closing brace and
1213/// swallows following siblings (issue #1524): the grammar tree carries no
1214/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1215/// close is located textually. Returns `None` when the text is unbalanced or
1216/// contains a construct the scanner deliberately does not interpret (raw
1217/// strings) -- callers treat that as "cannot partition" and keep the
1218/// un-split recovery.
1219fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1220    let bytes = source.as_bytes();
1221    if bytes.get(open_byte) != Some(&b'{') {
1222        return None;
1223    }
1224    let mut depth = 0usize;
1225    let mut i = open_byte;
1226    while i < bytes.len() {
1227        match bytes[i] {
1228            b'{' => depth += 1,
1229            b'}' => {
1230                depth = depth.checked_sub(1)?;
1231                if depth == 0 {
1232                    return Some(i);
1233                }
1234            }
1235            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1236                while i < bytes.len() && bytes[i] != b'\n' {
1237                    i += 1;
1238                }
1239                continue;
1240            }
1241            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1242                i += 2;
1243                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1244                    i += 1;
1245                }
1246                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1247                continue;
1248            }
1249            quote @ (b'"' | b'\'') => {
1250                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1251                // bail out rather than mis-count.
1252                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1253                    return None;
1254                }
1255                i += 1;
1256                while i < bytes.len() && bytes[i] != quote {
1257                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1258                }
1259                if i >= bytes.len() {
1260                    return None;
1261                }
1262            }
1263            _ => {}
1264        }
1265        i += 1;
1266    }
1267    None
1268}
1269
1270fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1271    let mut name = None;
1272    let mut colon_count = 0;
1273    let mut access_count = 0;
1274    for index in 0..node.child_count() {
1275        let child = node.child(index)?;
1276        match child.kind() {
1277            "identifier" | "type_identifier" if child.is_named() => {
1278                if name.is_some() {
1279                    return None;
1280                }
1281                let candidate = normalize_cpp_whitespace(node_text(child, source));
1282                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1283                    return None;
1284                }
1285                name = Some(candidate);
1286            }
1287            "template_function" | "template_type" if child.is_named() => {
1288                if name.is_some() {
1289                    return None;
1290                }
1291                let candidate = child
1292                    .child_by_field_name("name")
1293                    .and_then(|name| direct_identifier_name(name, source))?;
1294                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1295                    return None;
1296                }
1297                name = Some(candidate);
1298            }
1299            ":" if !child.is_named() => colon_count += 1,
1300            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1301            _ => return None,
1302        }
1303    }
1304    (colon_count == 1 && access_count == 1)
1305        .then_some(name)
1306        .flatten()
1307}
1308
1309fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1310    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1311        return false;
1312    }
1313    node.named_child(0)
1314        .and_then(|child| direct_identifier_name(child, source))
1315        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1316}
1317
1318fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1319    (0..node.child_count()).any(|index| {
1320        node.child(index)
1321            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1322    })
1323}
1324
1325fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1326    match node.kind() {
1327        "type_identifier" | "identifier" | "namespace_identifier" => {
1328            recovered_base_atom(node, source)
1329        }
1330        "template_type" | "template_function" => node
1331            .child_by_field_name("name")
1332            .and_then(|name| recovered_malformed_base_name(name, source)),
1333        "ERROR" => None,
1334        "qualified_identifier" | "scoped_type_identifier" => {
1335            let suffix = node
1336                .child_by_field_name("name")
1337                .and_then(|name| recovered_malformed_base_name(name, source))?;
1338            let scope = node
1339                .child_by_field_name("scope")
1340                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1341            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1342                malformed_qualified_prefix(node, source)?
1343            } else {
1344                if malformed_qualified_prefix(node, source).is_some() {
1345                    return None;
1346                }
1347                scope
1348            };
1349            Some(format!("{prefix}::{suffix}"))
1350        }
1351        _ => None,
1352    }
1353}
1354
1355fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1356    if !matches!(
1357        node.kind(),
1358        "identifier" | "type_identifier" | "namespace_identifier"
1359    ) {
1360        return None;
1361    }
1362    let name = normalize_cpp_whitespace(node_text(node, source));
1363    (!name.is_empty()).then_some(name)
1364}
1365
1366fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1367    let mut prefix = None;
1368    let mut cursor = node.walk();
1369    for error in node
1370        .named_children(&mut cursor)
1371        .filter(|child| child.kind() == "ERROR")
1372    {
1373        if error.named_child_count() != 1 || prefix.is_some() {
1374            return None;
1375        }
1376        prefix = error
1377            .named_child(0)
1378            .and_then(|child| recovered_base_atom(child, source));
1379        prefix.as_ref()?;
1380    }
1381    prefix
1382}
1383
1384fn recover_exported_class_function_definition<'tree>(
1385    node: Node<'tree>,
1386    source: &str,
1387) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
1388    if node.kind() != "function_definition" {
1389        return None;
1390    }
1391    let type_node = node.child_by_field_name("type")?;
1392    let declarator = node.child_by_field_name("declarator")?;
1393
1394    if matches!(
1395        type_node.kind(),
1396        "class_specifier" | "struct_specifier" | "union_specifier"
1397    ) {
1398        let type_name = type_node
1399            .child_by_field_name("name")
1400            .and_then(|name| direct_identifier_name(name, source));
1401        let exported_macro_type = type_name
1402            .as_ref()
1403            .is_some_and(|name| cpp_export_macro_token(name));
1404        if exported_macro_type {
1405            let mut cursor = node.walk();
1406            let errors_before_declarator = node
1407                .named_children(&mut cursor)
1408                .filter(|child| {
1409                    child.kind() == "ERROR"
1410                        && child.start_byte() >= type_node.end_byte()
1411                        && child.end_byte() <= declarator.start_byte()
1412                })
1413                .collect::<Vec<_>>();
1414            if let Some(name) = errors_before_declarator
1415                .iter()
1416                .find_map(|error| displaced_exported_class_name(*error, source))
1417            {
1418                let raw_supertypes = errors_before_declarator
1419                    .iter()
1420                    .any(|error| malformed_inheritance_syntax(*error))
1421                    .then(|| recovered_malformed_base_name(declarator, source))
1422                    .flatten()
1423                    .map(|base| vec![base]);
1424                return Some((node, name, raw_supertypes));
1425            }
1426            if errors_before_declarator
1427                .iter()
1428                .any(|error| malformed_inheritance_syntax(*error))
1429            {
1430                return None;
1431            }
1432        }
1433        if !exported_macro_type
1434            && let Some(name) = type_name
1435            && !cpp_export_macro_token(&name)
1436            && let Some(base) =
1437                recovered_postfix_export_macro_base(node, type_node, declarator, source)
1438        {
1439            return Some((node, name, Some(vec![base])));
1440        }
1441        if let Some(name) = direct_identifier_name(declarator, source)
1442            && exported_macro_type
1443            && !cpp_export_macro_token(&name)
1444        {
1445            let raw_supertypes = exported_macro_type
1446                .then(|| recovered_single_base_after_declarator(node, declarator, source))
1447                .flatten()
1448                .map(|base| vec![base]);
1449            return Some((node, name, raw_supertypes));
1450        }
1451        if declarator.kind() == "parenthesized_declarator"
1452            && type_node
1453                .child_by_field_name("name")
1454                .and_then(|name| direct_identifier_name(name, source))
1455                .is_some_and(|name| cpp_export_macro_token(&name))
1456        {
1457            let body_start = node
1458                .child_by_field_name("body")
1459                .map(|body| body.start_byte())
1460                .unwrap_or(node.end_byte());
1461            let mut cursor = node.walk();
1462            if let Some(name) = node
1463                .named_children(&mut cursor)
1464                .filter(|child| {
1465                    child.kind() == "ERROR"
1466                        && child.start_byte() >= declarator.end_byte()
1467                        && child.end_byte() <= body_start
1468                })
1469                .find_map(|error| declarator_name_from_node(error, source))
1470            {
1471                return Some((node, name, None));
1472            }
1473        }
1474    }
1475
1476    let declarator_text = direct_identifier_name(declarator, source)?;
1477    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
1478        return None;
1479    }
1480    class_identifier_before_body(node, source).map(|name| (node, name, None))
1481}
1482
1483/// Whether `node` is the base type displaced into the declarator field of an
1484/// export-macro class that tree-sitter represented as a declaration or
1485/// function definition.
1486///
1487/// Declaration extraction already recovers this exact malformed envelope as a
1488/// class and records the declarator as its base. Reference extraction must use
1489/// the same structural fact instead of treating the node as a function name.
1490pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
1491    if !matches!(
1492        node.kind(),
1493        "qualified_identifier" | "scoped_type_identifier" | "template_type"
1494    ) {
1495        return false;
1496    }
1497    if let Some(function) = node.parent().filter(|parent| {
1498        parent.kind() == "function_definition"
1499            && parent
1500                .child_by_field_name("declarator")
1501                .is_some_and(|declarator| same_node(declarator, node))
1502    }) {
1503        return recover_exported_class_function_definition(function, source)
1504            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
1505    }
1506    let Some(initializer) = node.parent().filter(|parent| {
1507        parent.kind() == "init_declarator"
1508            && parent
1509                .child_by_field_name("declarator")
1510                .is_some_and(|declarator| same_node(declarator, node))
1511    }) else {
1512        return false;
1513    };
1514    initializer
1515        .parent()
1516        .filter(|parent| parent.kind() == "declaration")
1517        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
1518        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
1519}
1520
1521/// Recover the class item from a region reparse that still carries the
1522/// sentinel's synthetic function envelope.  An unknown class attribute can
1523/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
1524/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
1525/// then nested below that function, so direct class-child lookup is not enough.
1526struct CppSentinelReparsedClass<'tree> {
1527    declaration_node: Node<'tree>,
1528    name: String,
1529    body: Node<'tree>,
1530    raw_supertypes: Option<Vec<String>>,
1531}
1532
1533fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
1534    let mut cursor = root.walk();
1535    root.named_children(&mut cursor)
1536        .find(|child| child.kind() != "comment")
1537        .filter(|child| child.kind() == "template_declaration")
1538}
1539
1540fn cpp_sentinel_reparsed_class<'tree>(
1541    root: Node<'tree>,
1542    template_node: Option<Node<'tree>>,
1543    source: &str,
1544    ancestry: &ParentIndex<'tree>,
1545) -> Option<CppSentinelReparsedClass<'tree>> {
1546    let container = template_node.unwrap_or(root);
1547    let mut cursor = container.walk();
1548    for child in container.named_children(&mut cursor) {
1549        if matches!(
1550            child.kind(),
1551            "class_specifier" | "struct_specifier" | "union_specifier"
1552        ) {
1553            let name = class_like_name(child, source, ancestry)?;
1554            let body = cpp_body_node(child)?;
1555            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
1556                .then(|| extract_cpp_supertypes(child, source));
1557            return Some(CppSentinelReparsedClass {
1558                declaration_node: child,
1559                name,
1560                body,
1561                raw_supertypes,
1562            });
1563        }
1564        if child.kind() == "declaration"
1565            && let Some(class_node) = first_class_like_child(child)
1566        {
1567            let name = class_like_name(class_node, source, ancestry)?;
1568            let body = cpp_body_node(class_node)?;
1569            let raw_supertypes =
1570                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1571                    .then(|| extract_cpp_supertypes(class_node, source));
1572            return Some(CppSentinelReparsedClass {
1573                declaration_node: class_node,
1574                name,
1575                body,
1576                raw_supertypes,
1577            });
1578        }
1579        // Only when the nested class item carries its own body. A bodyless
1580        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
1581        // a function definition -- is the export-macro shape recovered by the
1582        // next arm, and must fall through to it rather than abort the search.
1583        if child.kind() == "function_definition"
1584            && let Some(class_node) = first_class_like_child(child)
1585            && let Some(body) = cpp_body_node(class_node)
1586            && let Some(name) = class_like_name(class_node, source, ancestry)
1587        {
1588            let raw_supertypes =
1589                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
1590                    .then(|| extract_cpp_supertypes(class_node, source));
1591            return Some(CppSentinelReparsedClass {
1592                declaration_node: class_node,
1593                name,
1594                body,
1595                raw_supertypes,
1596            });
1597        }
1598        if child.kind() == "function_definition"
1599            && let Some((_, name, raw_supertypes)) =
1600                recover_exported_class_function_definition(child, source)
1601        {
1602            let body = cpp_body_node(child)?;
1603            return Some(CppSentinelReparsedClass {
1604                declaration_node: child,
1605                name,
1606                body,
1607                raw_supertypes,
1608            });
1609        }
1610    }
1611    None
1612}
1613
1614fn recovered_postfix_export_macro_base(
1615    node: Node<'_>,
1616    type_node: Node<'_>,
1617    declarator: Node<'_>,
1618    source: &str,
1619) -> Option<String> {
1620    let mut cursor = node.walk();
1621    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
1622        child.kind() == "ERROR"
1623            && child.start_byte() >= type_node.end_byte()
1624            && child.end_byte() <= declarator.start_byte()
1625            && postfix_export_macro_inheritance(*child, source)
1626    });
1627    malformed_clauses.next()?;
1628    if malformed_clauses.next().is_some() {
1629        return None;
1630    }
1631    recovered_malformed_base_name(declarator, source)
1632}
1633
1634fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
1635    let mut macro_count = 0;
1636    let mut colon_count = 0;
1637    let mut access_count = 0;
1638    for index in 0..node.child_count() {
1639        let Some(child) = node.child(index) else {
1640            return false;
1641        };
1642        match child.kind() {
1643            "identifier" | "type_identifier" if child.is_named() => {
1644                let candidate = normalize_cpp_whitespace(node_text(child, source));
1645                if !cpp_export_macro_token(&candidate) {
1646                    return false;
1647                }
1648                macro_count += 1;
1649            }
1650            ":" if !child.is_named() => colon_count += 1,
1651            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1652            _ => return false,
1653        }
1654    }
1655    macro_count == 1 && colon_count == 1 && access_count == 1
1656}
1657
1658fn recovered_single_base_after_declarator(
1659    node: Node<'_>,
1660    declarator: Node<'_>,
1661    source: &str,
1662) -> Option<String> {
1663    let body_start = node
1664        .child_by_field_name("body")
1665        .map(|body| body.start_byte())
1666        .unwrap_or(node.end_byte());
1667    let mut cursor = node.walk();
1668    let mut bases = node
1669        .named_children(&mut cursor)
1670        .filter(|child| {
1671            child.kind() == "ERROR"
1672                && child.start_byte() >= declarator.end_byte()
1673                && child.end_byte() <= body_start
1674        })
1675        .filter_map(|error| displaced_exported_class_name(error, source));
1676    let base = bases.next()?;
1677    bases.next().is_none().then_some(base)
1678}
1679
1680fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
1681    (0..node.child_count()).any(|index| {
1682        node.child(index)
1683            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
1684    })
1685}
1686
1687pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
1688    recover_exported_class_function_definition(node, source).is_some()
1689}
1690
1691fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
1692    matches!(
1693        kind,
1694        "ERROR"
1695            | "preproc_if"
1696            | "preproc_ifdef"
1697            | "preproc_ifndef"
1698            | "preproc_else"
1699            | "preproc_elif"
1700    ) || (kind == "labeled_statement" && in_class_scope)
1701}
1702
1703pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
1704    if node.kind() != "declaration" {
1705        return false;
1706    }
1707    let mut ancestor = node.parent();
1708    while let Some(container) = ancestor {
1709        match container.kind() {
1710            "compound_statement" => {
1711                return container.parent().is_some_and(|class_container| {
1712                    is_recovered_exported_class_container(class_container, source)
1713                });
1714            }
1715            // These containers preserve ScopeInfo in visit_node. declaration_list is
1716            // the body container selected for a linkage specification.
1717            "template_declaration" | "linkage_specification" | "declaration_list" => {}
1718            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
1719            _ => return false,
1720        }
1721        ancestor = container.parent();
1722    }
1723    false
1724}
1725
1726pub fn recovered_exported_class_has_body(
1727    node: Node<'_>,
1728    source: &str,
1729    expected_name: &str,
1730) -> Option<bool> {
1731    match node.kind() {
1732        "function_definition" => {
1733            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
1734            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
1735        }
1736        "declaration" | "field_declaration" => {
1737            let recovered = recover_exported_class_declaration(node, source)?;
1738            (recovered.name == expected_name).then(|| recovered.body.is_some())
1739        }
1740        _ => None,
1741    }
1742}
1743
1744fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
1745    let body_start = node
1746        .child_by_field_name("body")
1747        .map(|body| body.start_byte())
1748        .unwrap_or(node.end_byte());
1749    let mut stack = Vec::new();
1750    for index in (0..node.named_child_count()).rev() {
1751        let Some(child) = node.named_child(index) else {
1752            continue;
1753        };
1754        if child.start_byte() >= body_start {
1755            continue;
1756        }
1757        stack.push(child);
1758    }
1759
1760    let mut best = None;
1761    while let Some(current) = stack.pop() {
1762        if matches!(current.kind(), "identifier" | "type_identifier") {
1763            let name = normalize_cpp_whitespace(node_text(current, source));
1764            if !name.is_empty()
1765                && !cpp_export_macro_token(&name)
1766                && !matches!(name.as_str(), "class" | "struct" | "union")
1767            {
1768                best = Some(name);
1769            }
1770            continue;
1771        }
1772
1773        for index in (0..current.named_child_count()).rev() {
1774            if let Some(child) = current.named_child(index)
1775                && child.start_byte() < body_start
1776            {
1777                stack.push(child);
1778            }
1779        }
1780    }
1781    best
1782}
1783
1784fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1785    if node.kind() == "declaration"
1786        && node
1787            .child_by_field_name("type")
1788            .or_else(|| first_class_like_child(node))
1789            .is_some_and(|type_node| {
1790                matches!(
1791                    type_node.kind(),
1792                    "class_specifier" | "struct_specifier" | "union_specifier"
1793                )
1794            })
1795        && let Some(name) = node
1796            .child_by_field_name("declarator")
1797            .and_then(|declarator| declarator_name_from_node(declarator, source))
1798        && !cpp_export_macro_token(&name)
1799    {
1800        return Some(name);
1801    }
1802
1803    if node.kind() == "function_definition"
1804        && node.child_by_field_name("type").is_some_and(|type_node| {
1805            matches!(
1806                type_node.kind(),
1807                "class_specifier" | "struct_specifier" | "union_specifier"
1808            )
1809        })
1810        && let Some(name) = node
1811            .child_by_field_name("declarator")
1812            .and_then(|declarator| direct_identifier_name(declarator, source))
1813        && !cpp_export_macro_token(&name)
1814    {
1815        return Some(name);
1816    }
1817
1818    let class_node = if matches!(
1819        node.kind(),
1820        "class_specifier" | "struct_specifier" | "union_specifier"
1821    ) {
1822        node
1823    } else {
1824        first_class_like_child(node)?
1825    };
1826    class_like_name_from_children(class_node, source)
1827}
1828
1829fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
1830    if !matches!(
1831        node.kind(),
1832        "identifier" | "field_identifier" | "type_identifier"
1833    ) {
1834        return None;
1835    }
1836    let name = normalize_cpp_whitespace(node_text(node, source));
1837    (!name.is_empty()).then_some(name)
1838}
1839
1840fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
1841    match node.kind() {
1842        "identifier" | "field_identifier" | "type_identifier" => {
1843            let name = normalize_cpp_whitespace(node_text(node, source));
1844            (!name.is_empty()).then_some(name)
1845        }
1846        _ => {
1847            let mut cursor = node.walk();
1848            node.named_children(&mut cursor)
1849                .find_map(|child| declarator_name_from_node(child, source))
1850        }
1851    }
1852}
1853
1854fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
1855    let mut cursor = node.walk();
1856    node.named_children(&mut cursor).find(|child| {
1857        matches!(
1858            child.kind(),
1859            "class_specifier" | "struct_specifier" | "union_specifier"
1860        )
1861    })
1862}
1863
1864/// Push a container's children as a `Siblings` cursor rather than snapshotting
1865/// them all with one shared scope: children are visited one at a time so a
1866/// `using namespace X;` sibling can affect the scope threaded to the siblings
1867/// that textually follow it (issue #1093).
1868fn push_cpp_container_work<'tree>(
1869    node: Node<'tree>,
1870    scope: ScopeInfo,
1871    stack: &mut Vec<CppWork<'tree>>,
1872) {
1873    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
1874}
1875
1876/// Materialize one selected named-child range with a tree-sitter cursor. The
1877/// cursor advances linearly across the parent's concrete children; repeatedly
1878/// asking for `named_child(index)` is quadratic on very wide generated nodes.
1879fn push_cpp_sibling_range<'tree>(
1880    parent: Node<'tree>,
1881    start_index: usize,
1882    end_index: usize,
1883    scope: ScopeInfo,
1884    stack: &mut Vec<CppWork<'tree>>,
1885) {
1886    let mut cursor = parent.walk();
1887    let children = parent
1888        .named_children(&mut cursor)
1889        .skip(start_index)
1890        .take(end_index.saturating_sub(start_index))
1891        .collect::<Vec<_>>()
1892        .into_iter();
1893    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
1894}
1895
1896/// Advance a `Siblings` cursor by one child: dispatch the current child under
1897/// the scope accumulated from its *earlier* siblings, then push a
1898/// continuation for the remaining siblings carrying the scope updated for
1899/// *this* child (only `using namespace X;` directives change it). Pushing the
1900/// continuation before the current child's own node work means the current
1901/// child's subtree fully drains (LIFO) before the next sibling is visited,
1902/// preserving left-to-right order.
1903fn advance_cpp_siblings<'tree>(
1904    mut siblings: CppSiblingsWork<'tree>,
1905    source: &str,
1906    stack: &mut Vec<CppWork<'tree>>,
1907) {
1908    let Some(child) = siblings.children.next() else {
1909        return;
1910    };
1911    let current_scope = siblings.scope.clone();
1912    if let Some(namespace) = cpp_using_namespace_target(child, source) {
1913        siblings.scope.visible_using_namespaces.push(namespace);
1914    }
1915    if !siblings.children.as_slice().is_empty() {
1916        stack.push(CppWork::Siblings(siblings));
1917    }
1918    stack.push(CppWork::Node(CppNodeWork {
1919        node: child,
1920        scope: current_scope,
1921    }));
1922}
1923
1924/// The namespace target of a `using namespace X;` directive, or `None` for
1925/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
1926/// kind. Distinguished structurally by the presence of the grammar's literal
1927/// `namespace` keyword token among the node's children -- not by inspecting
1928/// source text -- so it never misreads a member-importing using-declaration
1929/// as a namespace directive.
1930fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
1931    if node.kind() != "using_declaration" {
1932        return None;
1933    }
1934    let mut cursor = node.walk();
1935    let is_namespace_directive = node
1936        .children(&mut cursor)
1937        .any(|child| child.kind() == "namespace");
1938    if !is_namespace_directive {
1939        return None;
1940    }
1941    let target = node.named_child(0)?;
1942    // A leading `::` is the explicit-global marker, not part of the namespace
1943    // path (`using namespace ::std::chrono;`). Drop that AST token before
1944    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
1945    // keeps: storing the marker verbatim desynced the legacy package string from
1946    // the FqName bridge, which splits on `::` and drops the empty leading
1947    // component, tripping the package/short boundary assert when a bare-owner
1948    // out-of-line definition borrowed the directive's namespace (#1093 path).
1949    let start = target
1950        .child(0)
1951        .filter(|child| !child.is_named() && child.kind() == "::")
1952        .map_or(target.start_byte(), |marker| marker.end_byte());
1953    let text = normalize_cpp_whitespace(
1954        source
1955            .get(start..target.end_byte())
1956            .expect("using-directive target covers one source range"),
1957    );
1958    (!text.is_empty()).then_some(text)
1959}
1960
1961/// Every `using namespace X;` directive target in a file, in source order, for
1962/// resolution-time consumers that need the file's using-directives without the
1963/// per-position scope threading extraction does. Parses `source` fresh and
1964/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
1965/// keys on the grammar's `namespace` keyword token, not source text), so it
1966/// never misreads a member-importing `using X::Y;` as a namespace directive.
1967///
1968/// This is a whole-file over-approximation of what is in scope at any one point
1969/// (a directive nested inside a `namespace {}` block or a function body is still
1970/// reported), which is exactly what the #1134 identity reconciler wants: extra
1971/// candidate namespaces that no visible class confirms are harmless, and two
1972/// that both confirm are treated as a genuine ambiguity by the reconciler.
1973pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
1974    let mut parser = Parser::new();
1975    if parser
1976        .set_language(&tree_sitter_cpp::LANGUAGE.into())
1977        .is_err()
1978    {
1979        return Vec::new();
1980    }
1981    let Some(tree) = parser.parse(source, None) else {
1982        return Vec::new();
1983    };
1984    let mut namespaces = Vec::new();
1985    let mut seen = std::collections::HashSet::new();
1986    let mut stack = vec![tree.root_node()];
1987    while let Some(node) = stack.pop() {
1988        if let Some(namespace) = cpp_using_namespace_target(node, source)
1989            && seen.insert(namespace.clone())
1990        {
1991            namespaces.push(namespace);
1992        }
1993        let mut cursor = node.walk();
1994        stack.extend(node.named_children(&mut cursor));
1995    }
1996    namespaces
1997}
1998
1999pub struct CppVisitor<'a> {
2000    pub file: &'a ProjectFile,
2001    pub source: &'a str,
2002    pub parsed: &'a mut ParsedFile,
2003    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
2004    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
2005    ///
2006    /// C has no nested tag scope: a struct/union/enum tag declared inside
2007    /// another aggregate's member list has the scope of the outer declaration
2008    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
2009    /// therefore declares a file-scope `inner` that a later file-scope
2010    /// `struct inner *p;` legitimately references, where C++ would make the
2011    /// same shape a nested class `outer::inner`. Headers carry no compilation
2012    /// language of their own and keep the conservative C++ interpretation.
2013    pub c_tag_semantics: bool,
2014    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
2015    /// Byte regions whose contents were re-owned by a fragmented export-class
2016    /// recovery (#938): the scattered members between the fragmented
2017    /// declaration and its displaced closing brace are indexed as members of
2018    /// the recovered class by the region reparse, so the ordinary sibling walk
2019    /// must not ALSO index them as top-level declarations (that double-indexing
2020    /// made a scattered nested class ambiguous between `Inner` and
2021    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
2022    /// linear scan at visit time is fine.
2023    pub consumed_fragment_regions: Vec<(usize, usize)>,
2024}
2025
2026impl<'a> CppVisitor<'a> {
2027    #[allow(clippy::too_many_arguments)]
2028    pub fn visit_container(
2029        &mut self,
2030        node: Node<'_>,
2031        package_name: &str,
2032        module: Option<CodeUnit>,
2033        class_unit: Option<CodeUnit>,
2034        template_signature: Option<String>,
2035        visible_using_namespaces: Vec<String>,
2036    ) {
2037        let scope = ScopeInfo {
2038            package_name: package_name.to_string(),
2039            module,
2040            class_unit,
2041            template_signature,
2042            template_metadata: None,
2043            declarations_are_fields: false,
2044            recovered_specialization_member_scope: false,
2045            visible_using_namespaces,
2046        };
2047        self.run_container_work(node, scope);
2048    }
2049
2050    /// Whether a work node lies entirely inside a byte region consumed by a
2051    /// fragmented export-class recovery (#938); such nodes were already indexed
2052    /// as members of the recovered class by the region reparse.
2053    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
2054        self.consumed_fragment_regions
2055            .iter()
2056            .any(|&(start, end)| node.start_byte() >= start && node.end_byte() <= end)
2057    }
2058
2059    /// Drive the container work loop from an explicit seed scope to completion. The
2060    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
2061    /// stays alive for the whole traversal.
2062    fn run_container_work<'tree>(&mut self, node: Node<'tree>, scope: ScopeInfo) {
2063        // Every ancestor question this walk asks is answered from one index
2064        // built here. Asking tree-sitter itself costs the node's position in
2065        // the tree, which made a generated header with thousands of top-level
2066        // declarations quadratic (#2361). `node` is the root of its own tree in
2067        // every caller -- the file's tree, or a region reparse's -- and the
2068        // ascent below costs nothing in that case while keeping the index
2069        // correct if a caller ever seeds the walk lower down.
2070        let mut root = node;
2071        while let Some(parent) = root.parent() {
2072            root = parent;
2073        }
2074        let ancestry = ParentIndex::new(root);
2075        let mut stack = vec![CppWork::Container(CppContainer { node, scope })];
2076        while let Some(work) = stack.pop() {
2077            match work {
2078                CppWork::Container(container) => {
2079                    push_cpp_container_work(container.node, container.scope, &mut stack);
2080                }
2081                CppWork::Siblings(siblings) => {
2082                    advance_cpp_siblings(siblings, self.source, &mut stack);
2083                }
2084                CppWork::Node(work) => {
2085                    if self.node_is_inside_consumed_fragment(work.node) {
2086                        continue;
2087                    }
2088                    self.visit_node(work.node, &work.scope, &mut stack, &ancestry);
2089                }
2090            }
2091        }
2092    }
2093
2094    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
2095    /// it only when the entire region is member-shaped. This validation must happen
2096    /// before registering the recovered class because a rejected speculative range
2097    /// must not leak into the ordinary recovery path.
2098    fn reparse_fragmented_export_class_members(
2099        &self,
2100        fragmented: &FragmentedExportBody,
2101        class_name: &str,
2102    ) -> Option<FragmentedExportMembers> {
2103        if fragmented.reparse_start >= fragmented.reparse_end {
2104            return None;
2105        }
2106        let tree = cpp_reparse_fragmented_class_body(
2107            self.source,
2108            fragmented.reparse_start,
2109            fragmented.reparse_end,
2110        )?;
2111        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
2112            return Some(FragmentedExportMembers::Complete(tree));
2113        }
2114        let has_conditional_constructor = {
2115            let root = tree.root_node();
2116            let mut cursor = root.walk();
2117            root.named_children(&mut cursor).any(|child| {
2118                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
2119            })
2120        };
2121        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
2122    }
2123
2124    /// Index an already validated fragmented body as members of `class_unit`. The
2125    /// region reparse keeps each member's exact original byte and line positions.
2126    fn visit_fragmented_export_class_members(
2127        &mut self,
2128        outcome: FragmentedExportMembers,
2129        class_unit: CodeUnit,
2130        scope: &ScopeInfo,
2131    ) -> bool {
2132        let (tree, complete) = match outcome {
2133            FragmentedExportMembers::Complete(tree) => (tree, true),
2134            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
2135        };
2136        let root = tree.root_node();
2137        let class_name = class_unit.identifier().to_string();
2138        let member_scope = ScopeInfo {
2139            // A recovered export-macro class may borrow its namespace from an
2140            // earlier forward declaration even when the malformed node itself
2141            // sits at file scope. Use the recovered class identity as the
2142            // authoritative package for reparsed members as well.
2143            package_name: class_unit.package_name().to_string(),
2144            module: scope.module.clone(),
2145            class_unit: Some(class_unit),
2146            template_signature: scope.template_signature.clone(),
2147            template_metadata: None,
2148            declarations_are_fields: true,
2149            recovered_specialization_member_scope: false,
2150            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2151        };
2152        if !complete {
2153            // A conditional beginning immediately after an access label can
2154            // fragment one constructor declaration while leaving the rest of
2155            // the class body as unsafe statement soup. Recover only that
2156            // structurally proven constructor and leave the outer-tree
2157            // siblings unconsumed for their ordinary walk.
2158            let mut cursor = root.walk();
2159            let constructors = root
2160                .named_children(&mut cursor)
2161                .filter_map(|child| {
2162                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
2163                })
2164                .collect::<Vec<_>>();
2165            // The reparsed region is its own tree, so this drain walks it with
2166            // its own parent index.
2167            let reparsed_ancestry = ParentIndex::new(root);
2168            for constructor in constructors {
2169                let mut stack = Vec::new();
2170                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
2171                while let Some(work) = stack.pop() {
2172                    match work {
2173                        CppWork::Container(container) => {
2174                            push_cpp_container_work(container.node, container.scope, &mut stack);
2175                        }
2176                        CppWork::Siblings(siblings) => {
2177                            advance_cpp_siblings(siblings, self.source, &mut stack);
2178                        }
2179                        CppWork::Node(work) => {
2180                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
2181                        }
2182                    }
2183                }
2184            }
2185            return false;
2186        }
2187        self.run_container_work(root, member_scope);
2188        true
2189    }
2190
2191    fn visit_recovered_fragment_constructor<'tree>(
2192        &mut self,
2193        range: std::ops::Range<usize>,
2194        constructor_body: Node<'tree>,
2195        class_declaration: Node<'tree>,
2196        class_unit: &CodeUnit,
2197        scope: &ScopeInfo,
2198        ancestry: &ParentIndex<'tree>,
2199    ) {
2200        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
2201            return;
2202        };
2203        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
2204            tree.root_node(),
2205            range.start,
2206            class_unit.identifier(),
2207            self.source,
2208        ) else {
2209            return;
2210        };
2211        let member_scope = ScopeInfo {
2212            package_name: class_unit.package_name().to_string(),
2213            module: scope.module.clone(),
2214            class_unit: Some(class_unit.clone()),
2215            template_signature: scope.template_signature.clone(),
2216            template_metadata: None,
2217            declarations_are_fields: true,
2218            recovered_specialization_member_scope: false,
2219            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2220        };
2221        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
2222        else {
2223            return;
2224        };
2225        debug_assert_eq!(function.name, class_unit.identifier());
2226        let code_unit = function.code_unit(self.file.clone());
2227        self.parsed.add_code_unit_with_range(
2228            code_unit.clone(),
2229            Range {
2230                start_byte: function_declarator.start_byte(),
2231                end_byte: constructor_body.end_byte(),
2232                start_line: function_declarator.start_position().row + 1,
2233                end_line: constructor_body.end_position().row + 1,
2234            },
2235            None,
2236            None,
2237        );
2238        self.parsed.add_signature_with_metadata(
2239            code_unit.clone(),
2240            cpp_signature_metadata(
2241                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
2242                function_declarator,
2243                self.source,
2244                ancestry,
2245            )
2246            .with_declaration_only(false)
2247            .with_callable_linkage(cpp_callable_linkage(
2248                class_declaration,
2249                self.source,
2250                ancestry,
2251            )),
2252        );
2253        self.parsed.add_child(class_unit.clone(), code_unit);
2254    }
2255
2256    fn visit_recovered_fragment_prefix_members<'tree>(
2257        &mut self,
2258        root: Node<'tree>,
2259        constructor_start: usize,
2260        class_unit: &CodeUnit,
2261        scope: &ScopeInfo,
2262        ancestry: &ParentIndex<'tree>,
2263    ) {
2264        let member_scope = ScopeInfo {
2265            package_name: class_unit.package_name().to_string(),
2266            module: scope.module.clone(),
2267            class_unit: Some(class_unit.clone()),
2268            template_signature: scope.template_signature.clone(),
2269            template_metadata: None,
2270            declarations_are_fields: true,
2271            recovered_specialization_member_scope: false,
2272            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2273        };
2274        let mut stack = vec![root];
2275        while let Some(current) = stack.pop() {
2276            if current.kind() == "comment" || current.start_byte() >= constructor_start {
2277                continue;
2278            }
2279            if current.end_byte() <= constructor_start
2280                && current.kind() != "translation_unit"
2281                && current.kind() != "labeled_statement"
2282                && current.kind() != "ERROR"
2283            {
2284                let mut work_stack = Vec::new();
2285                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
2286                while let Some(work) = work_stack.pop() {
2287                    match work {
2288                        CppWork::Container(container) => {
2289                            push_cpp_container_work(
2290                                container.node,
2291                                container.scope,
2292                                &mut work_stack,
2293                            );
2294                        }
2295                        CppWork::Siblings(siblings) => {
2296                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
2297                        }
2298                        CppWork::Node(work) => {
2299                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
2300                        }
2301                    }
2302                }
2303                continue;
2304            }
2305            if matches!(
2306                current.kind(),
2307                "translation_unit" | "labeled_statement" | "ERROR"
2308            ) {
2309                let mut cursor = current.walk();
2310                stack.extend(current.named_children(&mut cursor));
2311            }
2312        }
2313    }
2314
2315    fn visit_node<'tree>(
2316        &mut self,
2317        node: Node<'tree>,
2318        scope: &ScopeInfo,
2319        stack: &mut Vec<CppWork<'tree>>,
2320        ancestry: &ParentIndex<'tree>,
2321    ) {
2322        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
2323            self.visit_node(node, &recovered_scope, stack, ancestry);
2324            return;
2325        }
2326        if let Some((class_node, name, fragmented)) = fragmented_plain_class_body(node, self.source)
2327        {
2328            let displaced_namespace_items =
2329                displaced_fragment_namespace_geometry(node, self.source)
2330                    .map(|boundary| boundary.namespace_items)
2331                    .unwrap_or_default();
2332            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
2333            let mut class_stack = Vec::new();
2334            // When the full body cannot be safely reparsed, the original class
2335            // node still proves ownership for its parser-visible prefix.
2336            let parser_visible_body =
2337                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
2338                    .then(|| cpp_body_node(class_node))
2339                    .flatten();
2340            let class_unit = self.visit_named_class_like_shape(
2341                class_node,
2342                name,
2343                parser_visible_body,
2344                true,
2345                Some(fragmented.class_range),
2346                Some(extract_cpp_supertypes(class_node, self.source)),
2347                scope,
2348                &mut class_stack,
2349                ancestry,
2350            );
2351            let member_scope = ScopeInfo {
2352                package_name: class_unit.package_name().to_string(),
2353                module: scope.module.clone(),
2354                class_unit: Some(class_unit.clone()),
2355                template_signature: scope.template_signature.clone(),
2356                template_metadata: None,
2357                declarations_are_fields: true,
2358                recovered_specialization_member_scope: false,
2359                visible_using_namespaces: scope.visible_using_namespaces.clone(),
2360            };
2361            let complete = outcome.is_some_and(|outcome| {
2362                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
2363            });
2364            if complete {
2365                self.consumed_fragment_regions
2366                    .push((node.start_byte(), fragmented.class_range.end_byte));
2367            } else {
2368                // A macro-constrained member can make the full body reparse
2369                // unsafe while tree-sitter still exposes later class members
2370                // as bounded siblings up to the displaced `}`/`;`. Keep the
2371                // structurally proven class/base declaration and re-own those
2372                // sibling nodes under it. They retain their original parser
2373                // nodes and exact ranges; the close boundary comes solely from
2374                // `fragmented_plain_class_body`.
2375                // Template wrappers put the escaped members beside the
2376                // template rather than beside its malformed declaration.
2377                for candidate in cpp_following_named_siblings(node, self.source) {
2378                    if candidate.start_byte() >= fragmented.reparse_end {
2379                        break;
2380                    }
2381                    if cpp_fragment_sibling_is_class_member(
2382                        candidate,
2383                        fragmented.reparse_end,
2384                        self.source,
2385                    ) {
2386                        self.recovered_class_sibling_scopes
2387                            .insert(candidate.id(), member_scope.clone());
2388                    }
2389                }
2390            }
2391            for item in displaced_namespace_items {
2392                self.recovered_class_sibling_scopes
2393                    .insert(item.id(), scope.clone());
2394            }
2395            stack.extend(class_stack);
2396            return;
2397        }
2398        match node.kind() {
2399            "template_declaration" => {
2400                if let Some(recovered) =
2401                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
2402                {
2403                    let mut template_scope = scope.clone();
2404                    template_scope.template_signature =
2405                        cpp_template_signature(node, recovered.declaration_node, self.source);
2406                    template_scope.template_metadata =
2407                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
2408                    let raw_supertypes =
2409                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
2410                    let mut class_stack = Vec::new();
2411                    let class_unit = self.visit_named_class_like_shape(
2412                        recovered.class_node,
2413                        recovered.name,
2414                        Some(recovered.body),
2415                        true,
2416                        Some(recovered.range),
2417                        raw_supertypes,
2418                        &template_scope,
2419                        &mut class_stack,
2420                        ancestry,
2421                    );
2422                    self.parsed.record_materialization(
2423                        MaterializationRecord::RecoveredDeclaration {
2424                            recovery: recovered.range,
2425                            unit: class_unit.clone(),
2426                        },
2427                    );
2428                    let member_scope = ScopeInfo {
2429                        package_name: template_scope.package_name.clone(),
2430                        module: template_scope.module.clone(),
2431                        class_unit: Some(class_unit.clone()),
2432                        template_signature: template_scope.template_signature.clone(),
2433                        template_metadata: None,
2434                        declarations_are_fields: true,
2435                        recovered_specialization_member_scope: recovered
2436                            .class_node
2437                            .child_by_field_name("name")
2438                            .is_some_and(|name| name.kind() == "template_type"),
2439                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
2440                    };
2441                    for tail_member in recovered.tail_members.into_iter().rev() {
2442                        stack.push(CppWork::Node(CppNodeWork {
2443                            node: tail_member,
2444                            scope: member_scope.clone(),
2445                        }));
2446                    }
2447                    stack.extend(class_stack);
2448                    for sibling in recovered.member_siblings {
2449                        self.recovered_class_sibling_scopes
2450                            .insert(sibling.id(), member_scope.clone());
2451                    }
2452                    return;
2453                }
2454                for index in (0..node.named_child_count()).rev() {
2455                    let Some(child) = node.named_child(index) else {
2456                        continue;
2457                    };
2458                    if matches!(
2459                        child.kind(),
2460                        "class_specifier"
2461                            | "struct_specifier"
2462                            | "union_specifier"
2463                            | "enum_specifier"
2464                            | "function_definition"
2465                            | "declaration"
2466                            | "field_declaration"
2467                            | "alias_declaration"
2468                            | "namespace_definition"
2469                    ) {
2470                        let mut template_scope = scope.clone();
2471                        template_scope.template_signature =
2472                            cpp_template_signature(node, child, self.source);
2473                        template_scope.template_metadata =
2474                            cpp_template_metadata(node, child, self.source, ancestry);
2475                        if let Some(recovered) = recover_fragmented_partial_specialization(
2476                            node,
2477                            child,
2478                            self.source,
2479                            ancestry,
2480                        ) {
2481                            let code_unit = self.visit_named_class_like_shape(
2482                                recovered.declaration_node,
2483                                recovered.name,
2484                                None,
2485                                true,
2486                                Some(recovered.range),
2487                                None,
2488                                &template_scope,
2489                                stack,
2490                                ancestry,
2491                            );
2492                            self.parsed.record_materialization(
2493                                MaterializationRecord::RecoveredDeclaration {
2494                                    recovery: recovered.range,
2495                                    unit: code_unit.clone(),
2496                                },
2497                            );
2498                            let mut member_scope = template_scope.clone();
2499                            member_scope.class_unit = Some(code_unit);
2500                            member_scope.declarations_are_fields = true;
2501                            member_scope.recovered_specialization_member_scope = true;
2502                            for prefix_member in recovered.prefix_members.into_iter().rev() {
2503                                stack.push(CppWork::Node(CppNodeWork {
2504                                    node: prefix_member,
2505                                    scope: member_scope.clone(),
2506                                }));
2507                            }
2508                            for sibling in recovered.member_siblings {
2509                                self.recovered_class_sibling_scopes
2510                                    .insert(sibling.id(), member_scope.clone());
2511                            }
2512                            for following in recovered.following_declarations.into_iter().rev() {
2513                                stack.push(CppWork::Node(CppNodeWork {
2514                                    node: following,
2515                                    scope: scope.clone(),
2516                                }));
2517                            }
2518                            return;
2519                        }
2520                        stack.push(CppWork::Node(CppNodeWork {
2521                            node: child,
2522                            scope: template_scope,
2523                        }));
2524                    }
2525                }
2526            }
2527            "namespace_definition" => self.visit_namespace(node, scope, stack),
2528            "linkage_specification" => {
2529                if let Some(body) = cpp_body_node(node) {
2530                    stack.push(CppWork::Container(CppContainer {
2531                        node: body,
2532                        scope: scope.clone(),
2533                    }));
2534                } else {
2535                    stack.push(CppWork::Container(CppContainer {
2536                        node,
2537                        scope: scope.clone(),
2538                    }));
2539                }
2540            }
2541            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
2542                self.visit_class_like(node, scope, stack, ancestry)
2543            }
2544            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
2545            // A bare namespace-begin sentinel can make tree-sitter promote the
2546            // wrapped declaration to an ERROR node instead of the usual bogus
2547            // function_definition envelope. Keep the recovery entry point on
2548            // the same structured path for both shapes; ordinary ERROR nodes
2549            // retain their declaration-preserving wrapper traversal when the
2550            // sentinel predicate does not match.
2551            "ERROR" => {
2552                if !self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
2553                    self.visit_macro_swallowed_function_declarations(node, scope);
2554                    stack.push(CppWork::Container(CppContainer {
2555                        node,
2556                        scope: scope.clone(),
2557                    }));
2558                }
2559            }
2560            "declaration" => {
2561                if scope.class_unit.is_some()
2562                    && scope.declarations_are_fields
2563                    && scope.recovered_specialization_member_scope
2564                    && let Some(alias_name) =
2565                        recovered_using_declaration_alias_name(node, self.source)
2566                {
2567                    self.add_type_aliases(node, scope, vec![alias_name]);
2568                } else {
2569                    self.visit_declaration(
2570                        node,
2571                        scope,
2572                        scope.declarations_are_fields,
2573                        stack,
2574                        ancestry,
2575                    )
2576                }
2577            }
2578            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
2579            "type_definition" | "alias_declaration" => {
2580                self.visit_type_declaration(node, scope, stack, ancestry)
2581            }
2582            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
2583            "preproc_include" => self.visit_include(node),
2584            kind if preserves_declaration_scope_through_wrapper(
2585                kind,
2586                scope.class_unit.is_some(),
2587            ) =>
2588            {
2589                // A preprocessor conditional gates every declaration inside it
2590                // on a configuration this analyzer never evaluates; record the
2591                // interval so declaration state can say so (issue #1476). The
2592                // else/elif branches are children of the `preproc_if` node, so
2593                // recording the openers covers every branch.
2594                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
2595                    let mut range = cpp_declaration_range(node);
2596                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
2597                        range.end_byte = boundary.end_byte;
2598                        range.end_line = boundary.end_line;
2599                    }
2600                    self.parsed.record_materialization(
2601                        MaterializationRecord::ConfigurationConditional { range },
2602                    );
2603                }
2604                stack.push(CppWork::Container(CppContainer {
2605                    node,
2606                    scope: scope.clone(),
2607                }))
2608            }
2609            _ => {}
2610        }
2611    }
2612
2613    fn visit_macro_swallowed_function_declarations<'tree>(
2614        &mut self,
2615        envelope: Node<'tree>,
2616        scope: &ScopeInfo,
2617    ) {
2618        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
2619            || envelope.kind() == "ERROR"
2620                && envelope
2621                    .parent()
2622                    .is_some_and(|parent| parent.kind() == "ERROR")
2623        {
2624            return;
2625        }
2626        let mut stack = (0..envelope.named_child_count())
2627            .filter_map(|index| envelope.named_child(index))
2628            .collect::<Vec<_>>();
2629        while let Some(node) = stack.pop() {
2630            if node.kind() == "function_declarator" {
2631                self.visit_error_swallowed_function_declaration(node, scope);
2632            }
2633            for index in 0..node.named_child_count() {
2634                if let Some(child) = node.named_child(index) {
2635                    stack.push(child);
2636                }
2637            }
2638        }
2639    }
2640
2641    fn visit_error_swallowed_function_declaration<'tree>(
2642        &mut self,
2643        node: Node<'tree>,
2644        scope: &ScopeInfo,
2645    ) -> bool {
2646        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
2647            return false;
2648        };
2649        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
2650            return false;
2651        };
2652        let root = tree.root_node();
2653        let mut cursor = root.walk();
2654        let declarations = root
2655            .named_children(&mut cursor)
2656            .filter(|child| child.kind() != "comment")
2657            .collect::<Vec<_>>();
2658        let [declaration] = declarations.as_slice() else {
2659            return false;
2660        };
2661        if declaration.kind() != "declaration"
2662            || declaration.has_error()
2663            || declaration.start_byte() != start
2664            || declaration.end_byte() != end
2665        {
2666            return false;
2667        }
2668        let recovery = cpp_recovery_window(self.source, start, end);
2669        self.record_recovered_declarations(recovery, |visitor| {
2670            visitor.run_container_work(root, scope.clone());
2671        });
2672        true
2673    }
2674
2675    fn visit_namespace<'tree>(
2676        &mut self,
2677        node: Node<'tree>,
2678        scope: &ScopeInfo,
2679        stack: &mut Vec<CppWork<'tree>>,
2680    ) {
2681        let name_node = node.child_by_field_name("name");
2682        let Some(name_node) = name_node else {
2683            if let Some(body) = cpp_body_node(node) {
2684                stack.push(CppWork::Container(CppContainer {
2685                    node: body,
2686                    scope: scope.clone(),
2687                }));
2688            }
2689            return;
2690        };
2691        // Diagnostic corpora contain deliberately ill-formed global namespace
2692        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
2693        // the leading global `::` as the first anonymous child. Honor that AST
2694        // boundary instead of appending the name to the lexical namespace;
2695        // appending produced legacy names such as `outer::::outer::inner`, which
2696        // could not round-trip through the structured FqName boundary.
2697        let explicitly_global = name_node
2698            .child(0)
2699            .is_some_and(|child| !child.is_named() && child.kind() == "::");
2700        let components = cpp_namespace_name_components(name_node, self.source);
2701        if components.is_empty() {
2702            return;
2703        }
2704        // One Module per namespace level. C++17's `namespace a::b { ... }` is
2705        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
2706        // shorthand must declare `a` as well as `a::b` -- extracting only the
2707        // innermost level left the enclosing namespace undeclared and made the
2708        // two spellings of one construct disagree (issue #1878).
2709        let mut package_name = if explicitly_global {
2710            String::new()
2711        } else {
2712            scope.package_name.clone()
2713        };
2714        let mut module = None;
2715        for component in components {
2716            let full_name = if package_name.is_empty() {
2717                component
2718            } else {
2719                format!("{package_name}::{component}")
2720            };
2721            let level = CodeUnit::new_fq(
2722                self.file.clone(),
2723                CodeUnitType::Module,
2724                "",
2725                full_name.clone(),
2726                cpp_namespace_fq(&full_name),
2727            );
2728            if !self.parsed.contains_declaration(&level) {
2729                self.parsed
2730                    .add_code_unit(level.clone(), node, self.source, None, None);
2731            }
2732            package_name = full_name;
2733            module = Some(level);
2734        }
2735
2736        let namespace_scope = ScopeInfo {
2737            package_name,
2738            module,
2739            // C++ never nests a namespace inside a class, so a surviving
2740            // class_unit here is always recovery bleed: a malformed-region
2741            // boundary upstream mis-scoped this namespace block. Keeping the
2742            // owner would mint the namespace's declarations as class members
2743            // under a re-appended package, desyncing the fq boundary assert
2744            // (#2306). Dropping it is identity-neutral for valid code, where
2745            // class_unit is always empty at a namespace definition.
2746            class_unit: None,
2747            template_signature: scope.template_signature.clone(),
2748            template_metadata: scope.template_metadata.clone(),
2749            declarations_are_fields: false,
2750            recovered_specialization_member_scope: false,
2751            visible_using_namespaces: scope.visible_using_namespaces.clone(),
2752        };
2753        let container = cpp_body_node(node).unwrap_or(node);
2754        stack.push(CppWork::Container(CppContainer {
2755            node: container,
2756            scope: namespace_scope,
2757        }));
2758    }
2759
2760    fn visit_class_like<'tree>(
2761        &mut self,
2762        node: Node<'tree>,
2763        scope: &ScopeInfo,
2764        stack: &mut Vec<CppWork<'tree>>,
2765        ancestry: &ParentIndex<'tree>,
2766    ) {
2767        let Some(name) = class_like_name(node, self.source, ancestry) else {
2768            return;
2769        };
2770        let name = qualified_class_name_chain(node, self.source, scope)
2771            .map(|chain| chain.join("$"))
2772            .unwrap_or(name);
2773        self.visit_named_class_like(node, name, scope, stack, ancestry);
2774    }
2775
2776    fn visit_named_class_like<'tree>(
2777        &mut self,
2778        node: Node<'tree>,
2779        name: String,
2780        scope: &ScopeInfo,
2781        stack: &mut Vec<CppWork<'tree>>,
2782        ancestry: &ParentIndex<'tree>,
2783    ) {
2784        let body = cpp_body_node(node);
2785        let definition_body_present = body.is_some();
2786        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
2787            .then(|| extract_cpp_supertypes(node, self.source));
2788        self.visit_named_class_like_shape(
2789            node,
2790            name,
2791            body,
2792            definition_body_present,
2793            None,
2794            raw_supertypes,
2795            scope,
2796            stack,
2797            ancestry,
2798        );
2799    }
2800
2801    /// Whether this class-like declaration is a C tag that belongs to the
2802    /// enclosing non-aggregate scope rather than to the aggregate it is
2803    /// lexically written inside.
2804    ///
2805    /// `class_specifier` is deliberately excluded: `class` is not C, so text
2806    /// that spells one in a `.c` file is not C code and keeps the C++ reading
2807    /// rather than getting a half-C identity.
2808    fn mints_tag_at_enclosing_c_scope(
2809        &self,
2810        declaration_node: Node<'_>,
2811        scope: &ScopeInfo,
2812        ancestry: &ParentIndex<'_>,
2813    ) -> bool {
2814        self.c_tag_semantics
2815            && scope.class_unit.is_some()
2816            && class_like_name(declaration_node, self.source, ancestry).is_some()
2817            && matches!(
2818                declaration_node.kind(),
2819                "struct_specifier" | "union_specifier" | "enum_specifier"
2820            )
2821    }
2822
2823    #[allow(clippy::too_many_arguments)]
2824    fn visit_named_class_like_shape<'tree>(
2825        &mut self,
2826        declaration_node: Node<'tree>,
2827        name: String,
2828        body: Option<Node<'tree>>,
2829        definition_body_present: bool,
2830        explicit_range: Option<Range>,
2831        raw_supertypes: Option<Vec<String>>,
2832        scope: &ScopeInfo,
2833        stack: &mut Vec<CppWork<'tree>>,
2834        ancestry: &ParentIndex<'tree>,
2835    ) -> CodeUnit {
2836        let displaced_macro_tail = if explicit_range.is_none() {
2837            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
2838        } else {
2839            None
2840        };
2841        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
2842        let recovered_scope = self.scope_for_recovered_exported_class(
2843            declaration_node,
2844            &name,
2845            definition_body_present,
2846            scope,
2847            ancestry,
2848        );
2849        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
2850        // aggregate's member list is declared at the enclosing non-aggregate
2851        // scope, not nested inside the aggregate. `scope.class_unit` is the
2852        // only aggregate carrier in this walk, so dropping it puts the tag at
2853        // the nearest enclosing non-aggregate scope -- the module at file or
2854        // namespace scope, and the same block-scope representation a
2855        // function-local aggregate already gets. The tag's own body scope
2856        // below still owns its members, so fields and enumerators are
2857        // unaffected.
2858        let c_tag_scope;
2859        let scope =
2860            if self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry) {
2861                c_tag_scope = ScopeInfo {
2862                    class_unit: None,
2863                    ..recovered_scope.clone()
2864                };
2865                &c_tag_scope
2866            } else {
2867                &recovered_scope
2868            };
2869        let short_name = if let Some(parent) = &scope.class_unit {
2870            cpp_join_nested_short(parent.short_name(), &name)
2871        } else {
2872            name.clone()
2873        };
2874        // A top-level out-of-line qualified class definition (`struct
2875        // Outer::Inner { ... }` inside its namespace, #2246) carries its
2876        // nesting chain as the `$`-joined display name; push one Type/Nested
2877        // segment per class so segment-pop owner navigation keeps working.
2878        // Every other leaf name stays opaque so a literal `$` in a source
2879        // identifier never crosses the split/join boundary (#2140).
2880        let qualified_chain = if scope.class_unit.is_none() {
2881            qualified_class_name_chain(declaration_node, self.source, scope)
2882                .filter(|chain| chain.join("$") == name)
2883        } else {
2884            None
2885        };
2886        let fq = if let Some(chain) = qualified_chain {
2887            let mut fq = FqName::new();
2888            cpp_push_package(&mut fq, &scope.package_name);
2889            let mut first = true;
2890            for component in chain {
2891                let kind = if first {
2892                    SegmentKind::Type
2893                } else {
2894                    SegmentKind::Nested
2895                };
2896                fq.push(cpp_segment(&component, kind));
2897                first = false;
2898            }
2899            fq
2900        } else {
2901            cpp_leaf_fq(
2902                &scope.package_name,
2903                scope.class_unit.as_ref(),
2904                &name,
2905                SegmentKind::Nested,
2906                SegmentKind::Type,
2907            )
2908        };
2909        let code_unit = CodeUnit::with_signature_and_fq(
2910            self.file.clone(),
2911            CodeUnitType::Class,
2912            scope.package_name.clone(),
2913            short_name,
2914            scope.template_signature.clone(),
2915            false,
2916            fq,
2917        );
2918        let has_body = definition_body_present;
2919        if !has_body && self.parsed.contains_declaration(&code_unit) {
2920            self.parsed.record_navigation_range(
2921                code_unit.clone(),
2922                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
2923            );
2924            return code_unit;
2925        }
2926        if has_body {
2927            if let Some(range) = explicit_range {
2928                self.parsed.replace_code_unit_with_range_deferred(
2929                    code_unit.clone(),
2930                    range,
2931                    None,
2932                    None,
2933                );
2934            } else {
2935                self.parsed.replace_code_unit_deferred(
2936                    code_unit.clone(),
2937                    declaration_node,
2938                    self.source,
2939                    None,
2940                    None,
2941                );
2942            }
2943        } else {
2944            self.parsed
2945                .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
2946        }
2947        if let Some(raw_supertypes) = raw_supertypes {
2948            self.parsed
2949                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
2950        }
2951        self.parsed.add_signature(
2952            code_unit.clone(),
2953            render_cpp_type_signature(
2954                declaration_node,
2955                self.source,
2956                scope.template_signature.as_deref(),
2957            ),
2958        );
2959        if let Some(metadata) = &scope.template_metadata {
2960            let primary_short_name = if let Some(parent) = &scope.class_unit {
2961                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
2962            } else {
2963                metadata.primary_name.clone()
2964            };
2965            let primary_fq_name = CodeUnit::new(
2966                self.file.clone(),
2967                CodeUnitType::Class,
2968                scope.package_name.clone(),
2969                primary_short_name,
2970            )
2971            .fq_name();
2972            let mut metadata = metadata.clone();
2973            metadata.primary_fq_name = primary_fq_name;
2974            self.parsed
2975                .set_cpp_template_metadata(code_unit.clone(), metadata);
2976        }
2977        if let Some(parent) = &scope.class_unit {
2978            self.parsed.add_child(parent.clone(), code_unit.clone());
2979        } else if let Some(module) = &scope.module {
2980            self.parsed.add_child(module.clone(), code_unit.clone());
2981        }
2982
2983        if let Some(body) = body {
2984            let mut nested_scope = scope.clone();
2985            nested_scope.class_unit = Some(code_unit.clone());
2986            nested_scope.template_signature = scope.template_signature.clone();
2987            // Template metadata describes the class just created. It must not
2988            // leak into ordinary nested declarations in that class's body.
2989            // Recovered export-macro specializations carry a separate scope bit
2990            // for their declaration-shaped body members.
2991            nested_scope.template_metadata = None;
2992            // Export-macro class bodies recovered from a function_definition use
2993            // compound_statement children, whose direct fields are declarations.
2994            nested_scope.recovered_specialization_member_scope =
2995                scope.template_metadata.as_ref().is_some_and(|metadata| {
2996                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
2997                });
2998            nested_scope.declarations_are_fields =
2999                is_recovered_exported_class_container(declaration_node, self.source)
3000                    || nested_scope.recovered_specialization_member_scope;
3001            if let Some(displaced) = displaced_macro_tail {
3002                // A macro-shaped field without a source semicolon can make
3003                // tree-sitter consume the real class terminator as an ERROR
3004                // inside that field, then retain following namespace items as
3005                // later field-list children. Drain the proven class prefix
3006                // first and re-own only the structured tail with the outer
3007                // scope. The tail is pushed first because the work stack is
3008                // LIFO.
3009                push_cpp_sibling_range(
3010                    body,
3011                    displaced.split_index,
3012                    usize::MAX,
3013                    scope.clone(),
3014                    stack,
3015                );
3016                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
3017            } else {
3018                stack.push(CppWork::Container(CppContainer {
3019                    node: body,
3020                    scope: nested_scope,
3021                }));
3022            }
3023        }
3024        if declaration_node.kind() == "enum_specifier" {
3025            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
3026            if !self.has_enum_enumerator_units(&code_unit) {
3027                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
3028            }
3029        }
3030        code_unit
3031    }
3032
3033    fn has_enum_enumerator_units(&self, parent: &CodeUnit) -> bool {
3034        let prefix = format!("{}.", parent.short_name());
3035        let parent_short = parent.short_name();
3036        self.parsed.declarations().iter().any(|unit| {
3037            unit.kind() == CodeUnitType::Field
3038                && unit.source() == parent.source()
3039                && unit.package_name() == parent.package_name()
3040                && if parent_short.is_empty() {
3041                    // Anonymous enum/union parent: its enumerators carry bare
3042                    // short names (#2140), so presence means any ownerless
3043                    // field in this file.
3044                    !unit.short_name().contains(['.', '$'])
3045                } else {
3046                    unit.short_name().starts_with(&prefix)
3047                }
3048        })
3049    }
3050
3051    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
3052        walk_named_tree_preorder(node, false, |child| {
3053            if child.kind() != "enumerator" {
3054                return WalkControl::Continue;
3055            }
3056            let Some(name_node) = child.child_by_field_name("name") else {
3057                return WalkControl::Continue;
3058            };
3059            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
3060            if name.is_empty() {
3061                return WalkControl::Continue;
3062            }
3063            let code_unit = CodeUnit::new_fq(
3064                self.file.clone(),
3065                CodeUnitType::Field,
3066                scope.package_name.clone(),
3067                cpp_join_member_short(parent.short_name(), &name),
3068                parent
3069                    .fq()
3070                    .clone()
3071                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
3072            );
3073            if self.parsed.contains_declaration(&code_unit) {
3074                return WalkControl::Continue;
3075            }
3076            self.parsed.add_code_unit(
3077                code_unit.clone(),
3078                child,
3079                self.source,
3080                Some(parent.clone()),
3081                None,
3082            );
3083            self.parsed.add_signature(
3084                code_unit,
3085                normalize_cpp_whitespace(node_text(child, self.source)),
3086            );
3087            WalkControl::Continue
3088        });
3089    }
3090
3091    fn visit_enum_enumerators_from_text(
3092        &mut self,
3093        node: Node<'_>,
3094        scope: &ScopeInfo,
3095        parent: &CodeUnit,
3096    ) {
3097        let text = node_text(node, self.source);
3098        let Some((_, body)) = text.split_once('{') else {
3099            return;
3100        };
3101        let Some((body, _)) = body.rsplit_once('}') else {
3102            return;
3103        };
3104        for entry in body.split(',') {
3105            let trimmed = entry.trim();
3106            let name = trimmed
3107                .split('=')
3108                .next()
3109                .unwrap_or("")
3110                .split_whitespace()
3111                .next()
3112                .unwrap_or("");
3113            if name.is_empty() {
3114                continue;
3115            }
3116            let code_unit = CodeUnit::new_fq(
3117                self.file.clone(),
3118                CodeUnitType::Field,
3119                scope.package_name.clone(),
3120                cpp_join_member_short(parent.short_name(), name),
3121                parent
3122                    .fq()
3123                    .clone()
3124                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
3125            );
3126            if self.parsed.contains_declaration(&code_unit) {
3127                continue;
3128            }
3129            self.parsed.add_code_unit(
3130                code_unit.clone(),
3131                node,
3132                self.source,
3133                Some(parent.clone()),
3134                None,
3135            );
3136            self.parsed.add_signature(code_unit, trimmed.to_string());
3137        }
3138    }
3139
3140    fn visit_function_definition<'tree>(
3141        &mut self,
3142        node: Node<'tree>,
3143        scope: &ScopeInfo,
3144        stack: &mut Vec<CppWork<'tree>>,
3145        ancestry: &ParentIndex<'tree>,
3146    ) {
3147        // A file-scope object-like macro sentinel the parser cannot see (issue
3148        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
3149        // prefixes as a bogus `function_definition` that swallows real namespaces,
3150        // classes, and members. Reparse the swallowed interior as C++ items so the
3151        // ordinary declaration visitors index it with byte/line-exact ownership.
3152        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3153            return;
3154        }
3155        if node.has_error() {
3156            self.visit_macro_swallowed_function_declarations(node, scope);
3157        }
3158        if let Some((class_node, name, raw_supertypes)) =
3159            recover_exported_class_function_definition(node, self.source)
3160        {
3161            let body = cpp_body_node(class_node);
3162            let displaced_namespace = cpp_body_node(node)
3163                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
3164            let fragmented = cpp_body_node(node).and_then(|body| {
3165                fragmented_export_function_body_region(
3166                    node,
3167                    body,
3168                    self.source,
3169                    displaced_namespace.as_ref(),
3170                )
3171            });
3172            // The recovery tuple's first node is the class-like type when the
3173            // parser exposes one, but the synthetic wrapper owns the compound
3174            // statement that contains the truncated class body. Use the
3175            // wrapper body for fragmented-member detection; retain the
3176            // class-node body for the ordinary (non-fragmented) path below.
3177            if let Some(fragmented) = fragmented {
3178                // The lifted sibling no longer sits below the parser-visible
3179                // namespace node. Restore the current parent scope when the
3180                // ordinary work walk reaches that class.
3181                if let Some(boundary) = fragmented_export_sibling_class_boundary(node, self.source)
3182                    .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
3183                {
3184                    let mut boundary_scope = scope.clone();
3185                    for sibling in cpp_following_named_siblings(node, self.source) {
3186                        if sibling.start_byte() >= boundary.start_byte() {
3187                            break;
3188                        }
3189                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
3190                            boundary_scope.visible_using_namespaces.push(namespace);
3191                        }
3192                    }
3193                    self.recovered_class_sibling_scopes
3194                        .insert(boundary.id(), boundary_scope);
3195                }
3196                let mut recovered_constructor = None;
3197                let mut recovered_prefix_tree = None;
3198                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
3199                {
3200                    Some(FragmentedExportMembers::Complete(tree)) => {
3201                        if let Some(body) = body
3202                            && let Some(range) =
3203                                cpp_reparsed_synthetic_initializer_constructor_range(
3204                                    tree.root_node(),
3205                                    &name,
3206                                    self.source,
3207                                    body.end_byte(),
3208                                )
3209                        {
3210                            recovered_constructor = Some(range);
3211                            recovered_prefix_tree = Some(tree);
3212                            None
3213                        } else {
3214                            Some(FragmentedExportMembers::Complete(tree))
3215                        }
3216                    }
3217                    outcome => outcome,
3218                };
3219                let mut class_stack = Vec::new();
3220                let class_unit = self.visit_named_class_like_shape(
3221                    class_node,
3222                    name,
3223                    None,
3224                    true,
3225                    Some(fragmented.class_range),
3226                    raw_supertypes,
3227                    scope,
3228                    &mut class_stack,
3229                    ancestry,
3230                );
3231                self.parsed
3232                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3233                        recovery: fragmented.class_range,
3234                        unit: class_unit.clone(),
3235                    });
3236                let complete = outcome.is_some_and(|outcome| {
3237                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
3238                });
3239                if complete {
3240                    self.consumed_fragment_regions
3241                        .push((node.start_byte(), fragmented.class_range.end_byte));
3242                } else {
3243                    // The reparse can fail when the first constructor or a
3244                    // method body is split into statement-shaped siblings.
3245                    // Keep the recovered class envelope, but do not visit the
3246                    // synthetic wrapper body: its initializer expressions can
3247                    // look like same-named member functions (for example
3248                    // `Token.location(loc)`). Re-own only the original sibling
3249                    // nodes that fall inside the proven class range. Their CST
3250                    // shapes retain the real field/function kinds and ranges.
3251                    let member_scope = ScopeInfo {
3252                        package_name: class_unit.package_name().to_string(),
3253                        module: scope.module.clone(),
3254                        class_unit: Some(class_unit.clone()),
3255                        template_signature: scope.template_signature.clone(),
3256                        template_metadata: None,
3257                        declarations_are_fields: true,
3258                        recovered_specialization_member_scope: false,
3259                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
3260                    };
3261                    for candidate in cpp_following_named_siblings(node, self.source) {
3262                        if candidate.start_byte() >= fragmented.reparse_end {
3263                            break;
3264                        }
3265                        if cpp_fragment_sibling_is_class_member(
3266                            candidate,
3267                            fragmented.reparse_end,
3268                            self.source,
3269                        ) {
3270                            self.recovered_class_sibling_scopes
3271                                .insert(candidate.id(), member_scope.clone());
3272                        }
3273                    }
3274                    if let Some(range) = recovered_constructor
3275                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
3276                    {
3277                        self.visit_recovered_fragment_prefix_members(
3278                            prefix_tree.root_node(),
3279                            range.start,
3280                            &class_unit,
3281                            scope,
3282                            ancestry,
3283                        );
3284                        self.visit_recovered_fragment_constructor(
3285                            range,
3286                            body,
3287                            class_node,
3288                            &class_unit,
3289                            scope,
3290                            ancestry,
3291                        );
3292                    }
3293                }
3294                if let Some(boundary) = displaced_namespace {
3295                    for item in boundary.namespace_items {
3296                        self.recovered_class_sibling_scopes
3297                            .insert(item.id(), scope.clone());
3298                    }
3299                }
3300                stack.extend(class_stack);
3301                return;
3302            }
3303            let mut stack = Vec::new();
3304            let class_unit = self.visit_named_class_like_shape(
3305                class_node,
3306                name,
3307                body,
3308                body.is_some(),
3309                None,
3310                raw_supertypes,
3311                scope,
3312                &mut stack,
3313                ancestry,
3314            );
3315            self.parsed
3316                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3317                    recovery: cpp_declaration_range(node),
3318                    unit: class_unit,
3319                });
3320            // Issue #1524: the bogus `function_definition` body can run past
3321            // the class's true closing brace (the parse ends it with a
3322            // zero-width `MISSING "}"`), swallowing following namespace-scope
3323            // siblings -- they would index as members of the recovered class.
3324            // When the body's text-balanced close lands before the body's own
3325            // end, re-own the swallowed tail with the outer scope instead.
3326            if let Some(body) = body
3327                && let Some(class_close) = cpp_matching_close_brace(self.source, body.start_byte())
3328                && class_close < body.end_byte()
3329            {
3330                let split = {
3331                    let mut cursor = body.walk();
3332                    body.named_children(&mut cursor)
3333                        .position(|child| child.start_byte() > class_close)
3334                };
3335                if let Some(split) = split {
3336                    // The seeded work is a single Container over the whole
3337                    // body with the class scope; replace it with the bounded
3338                    // head (class scope) plus the swallowed tail (outer
3339                    // scope). Push tail first so the head drains first.
3340                    let seeded = stack.pop();
3341                    match seeded {
3342                        Some(CppWork::Container(container)) => {
3343                            push_cpp_sibling_range(
3344                                body,
3345                                split,
3346                                usize::MAX,
3347                                scope.clone(),
3348                                &mut stack,
3349                            );
3350                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
3351                        }
3352                        // visit_named_class_like_shape always seeds exactly
3353                        // one Container when a body is present.
3354                        _ => unreachable!("exported-class seed is always one Container"),
3355                    }
3356                }
3357            }
3358            while let Some(work) = stack.pop() {
3359                match work {
3360                    CppWork::Container(container) => {
3361                        push_cpp_container_work(container.node, container.scope, &mut stack);
3362                    }
3363                    CppWork::Siblings(siblings) => {
3364                        advance_cpp_siblings(siblings, self.source, &mut stack);
3365                    }
3366                    CppWork::Node(work) => {
3367                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
3368                    }
3369                }
3370            }
3371            return;
3372        }
3373        let recovered_constraint_constructor =
3374            cpp_recovered_template_macro_constructor(node, self.source);
3375        let declarator = recovered_constraint_constructor
3376            .map(|(declarator, _)| declarator)
3377            .or_else(|| node.child_by_field_name("declarator"));
3378        let Some(declarator) = declarator else {
3379            self.visit_malformed_function_definition_container(node, scope, stack);
3380            return;
3381        };
3382        let Some(function_declarator) = extract_function_declarator(declarator) else {
3383            self.visit_malformed_function_definition_container(node, scope, stack);
3384            return;
3385        };
3386        let function = if let Some((_, callable_name)) =
3387            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
3388        {
3389            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
3390        } else {
3391            extract_function_info(function_declarator, self.source, scope)
3392        };
3393        let Some(mut function) = function else {
3394            self.visit_malformed_function_definition_container(node, scope, stack);
3395            return;
3396        };
3397        if let Some((_, template_parameter)) = recovered_constraint_constructor {
3398            function.signature = format!(
3399                "template <{}>{}",
3400                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
3401                function.signature
3402            );
3403        }
3404        let code_unit = function.code_unit(self.file.clone());
3405        // Keep an earlier same-file prototype as another physical occurrence
3406        // of this callable. `CodeUnit` already identifies the role-neutral
3407        // overload, while ranges and signature metadata describe its
3408        // declaration/definition occurrences.
3409        self.parsed
3410            .add_code_unit(code_unit.clone(), node, self.source, None, None);
3411        let signature = if recovered_constraint_constructor.is_some() {
3412            normalize_cpp_whitespace(node_text(function_declarator, self.source))
3413        } else {
3414            render_cpp_function_display_signature_from_node(
3415                node,
3416                self.source,
3417                scope.template_signature.as_deref(),
3418                true,
3419                ancestry,
3420            )
3421        };
3422        self.parsed.add_signature_with_metadata(
3423            code_unit.clone(),
3424            cpp_signature_metadata(signature, function_declarator, self.source, ancestry)
3425                .with_declaration_only(false)
3426                .with_callable_linkage(cpp_callable_linkage(node, self.source, ancestry)),
3427        );
3428        if let Some(parent) = &scope.class_unit {
3429            self.parsed.add_child(parent.clone(), code_unit);
3430        } else if let Some(module) = &scope.module {
3431            self.parsed.add_child(module.clone(), code_unit);
3432        }
3433    }
3434
3435    /// Recover the namespace lost when tree-sitter promotes an export-macro
3436    /// class definition to a root-level `function_definition`.  Only a
3437    /// body-bearing, top-level recovery may borrow a namespace, and only when
3438    /// one earlier namespace-scope forward declaration proves the identity.
3439    fn scope_for_recovered_exported_class<'tree>(
3440        &self,
3441        node: Node<'tree>,
3442        name: &str,
3443        definition_body_present: bool,
3444        scope: &ScopeInfo,
3445        ancestry: &ParentIndex<'tree>,
3446    ) -> ScopeInfo {
3447        if !definition_body_present
3448            || !scope.package_name.is_empty()
3449            || scope.class_unit.is_some()
3450            || !(is_recovered_exported_class_container(node, self.source)
3451                || matches!(node.kind(), "declaration" | "field_declaration")
3452                    && recover_exported_class_declaration(node, self.source).is_some()
3453                || matches!(
3454                    node.kind(),
3455                    "class_specifier" | "struct_specifier" | "union_specifier"
3456                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
3457                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
3458                        name_node,
3459                        self.source,
3460                    )))
3461                }) || ancestry.parent(node).is_some_and(|parent| {
3462                    matches!(parent.kind(), "declaration" | "field_declaration")
3463                        && recover_exported_class_declaration(parent, self.source).is_some()
3464                        || is_recovered_exported_class_container(parent, self.source)
3465                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
3466        {
3467            return scope.clone();
3468        }
3469        let Some(package_name) =
3470            unique_earlier_cpp_namespace_forward(node, name, self.source, ancestry)
3471        else {
3472            return scope.clone();
3473        };
3474
3475        let module = CodeUnit::new_fq(
3476            self.file.clone(),
3477            CodeUnitType::Module,
3478            "",
3479            package_name.clone(),
3480            cpp_namespace_fq(&package_name),
3481        );
3482        let mut recovered = scope.clone();
3483        recovered.package_name = package_name;
3484        recovered.module = Some(module);
3485        recovered
3486    }
3487
3488    fn visit_malformed_function_definition_container<'tree>(
3489        &mut self,
3490        node: Node<'tree>,
3491        scope: &ScopeInfo,
3492        stack: &mut Vec<CppWork<'tree>>,
3493    ) {
3494        let Some(body) = cpp_body_node(node) else {
3495            return;
3496        };
3497        if !cpp_contains_namespace_definition(body) {
3498            return;
3499        }
3500        stack.push(CppWork::Container(CppContainer {
3501            node: body,
3502            scope: scope.clone(),
3503        }));
3504    }
3505
3506    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
3507    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
3508    /// emits for a sentinel-prefixed region, reparse the interior after the
3509    /// sentinel identifier as real C++ items -- confined to the region so
3510    /// every reparsed node keeps its original byte/line position -- and run the
3511    /// ordinary container visitation over the result. Returns `true` when it fired
3512    /// (the caller must then skip normal function processing). Nested sentinel
3513    /// regions recover recursively: the reparsed interior is walked through the
3514    /// same `visit_function_definition` path, so a sentinel inside the region hits
3515    /// this recovery again.
3516    /// Runs `reparse_walk` and records every declaration it mints as a
3517    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
3518    /// `recovery` (issue #1657). A reparsed sentinel region has no single
3519    /// recovered envelope unit: the ordinary visitors mint namespaces,
3520    /// classes, and members directly from the reparsed tree, so the walk's
3521    /// declaration delta is the recovered set. Records are ordered by
3522    /// declaration start byte so the parse product stays deterministic.
3523    fn record_recovered_declarations(
3524        &mut self,
3525        recovery: Range,
3526        reparse_walk: impl FnOnce(&mut Self),
3527    ) {
3528        let before = self.parsed.declarations().clone();
3529        reparse_walk(self);
3530        let mut minted: Vec<CodeUnit> = self
3531            .parsed
3532            .declarations()
3533            .iter()
3534            .filter(|unit| !before.contains(*unit))
3535            .cloned()
3536            .collect();
3537        minted.sort_by_cached_key(|unit| {
3538            let start = self
3539                .parsed
3540                .declaration_ranges(unit)
3541                .first()
3542                .map(|range| range.start_byte)
3543                .unwrap_or(usize::MAX);
3544            (start, unit.fq_name().to_string())
3545        });
3546        for unit in minted {
3547            self.parsed
3548                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3549                    recovery,
3550                    unit,
3551                });
3552        }
3553    }
3554
3555    fn visit_sentinel_macro_region<'tree>(
3556        &mut self,
3557        node: Node<'tree>,
3558        scope: &ScopeInfo,
3559        stack: &mut Vec<CppWork<'tree>>,
3560        ancestry: &ParentIndex<'tree>,
3561    ) -> bool {
3562        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
3563            return true;
3564        }
3565        if let Some((
3566            reparse_start,
3567            class_start,
3568            body_start,
3569            class_close_start,
3570            class_close_end,
3571            class_close_line,
3572        )) = cpp_sentinel_macro_class_region(node, self.source)
3573        {
3574            let Some(class_tree) =
3575                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
3576            else {
3577                return false;
3578            };
3579            let class_root = class_tree.root_node();
3580            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
3581            // A region reparse is its own tree and needs its own parent index.
3582            let class_ancestry = ParentIndex::new(class_root);
3583            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
3584                class_root,
3585                template_node,
3586                self.source,
3587                &class_ancestry,
3588            ) else {
3589                return false;
3590            };
3591            let class_node = reparsed_class.declaration_node;
3592            let name = reparsed_class.name;
3593            let mut class_scope = scope.clone();
3594            if let Some(template_node) = template_node {
3595                class_scope.template_signature =
3596                    cpp_template_signature(template_node, class_node, self.source);
3597                class_scope.template_metadata =
3598                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
3599            }
3600            let Some(body_tree) =
3601                cpp_reparse_region_items(self.source, body_start, class_close_start)
3602            else {
3603                return false;
3604            };
3605            let raw_supertypes = reparsed_class.raw_supertypes;
3606            let class_range = Range {
3607                start_byte: class_start,
3608                end_byte: class_close_end,
3609                start_line: class_node.start_position().row + 1,
3610                end_line: class_close_line,
3611            };
3612            let class_scope = self.scope_for_recovered_exported_class(
3613                class_node,
3614                &name,
3615                true,
3616                &class_scope,
3617                ancestry,
3618            );
3619            let mut class_stack = Vec::new();
3620            let class_unit = self.visit_named_class_like_shape(
3621                class_node,
3622                name,
3623                None,
3624                true,
3625                Some(class_range),
3626                raw_supertypes,
3627                &class_scope,
3628                &mut class_stack,
3629                ancestry,
3630            );
3631            self.parsed
3632                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3633                    recovery: class_range,
3634                    unit: class_unit.clone(),
3635                });
3636            let member_scope = ScopeInfo {
3637                package_name: class_scope.package_name.clone(),
3638                module: class_scope.module.clone(),
3639                class_unit: Some(class_unit),
3640                template_signature: class_scope.template_signature.clone(),
3641                template_metadata: None,
3642                declarations_are_fields: true,
3643                recovered_specialization_member_scope: false,
3644                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
3645            };
3646            self.run_container_work(body_tree.root_node(), member_scope);
3647            // Register only after the padded body reparse: its nodes deliberately
3648            // retain offsets inside the consumed region and must be visited first.
3649            self.consumed_fragment_regions
3650                .push((node.start_byte(), class_close_end));
3651            // An ERROR envelope can hold real sibling declarations after the
3652            // recovered class's close (the suffix-reparse boundary in
3653            // `cpp_sentinel_macro_class_region` partitions, it does not
3654            // consume). Walk the envelope's remaining children normally; the
3655            // consumed region above keeps the recovered class from being
3656            // indexed twice.
3657            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
3658                stack.push(CppWork::Container(CppContainer {
3659                    node,
3660                    scope: scope.clone(),
3661                }));
3662            }
3663            return true;
3664        }
3665        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
3666            return false;
3667        };
3668        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
3669            return false;
3670        };
3671        let root = tree.root_node();
3672        if !cpp_reparsed_items_are_indexable(root, self.source) {
3673            return false;
3674        }
3675        let recovery = cpp_recovery_window(self.source, start, end);
3676        self.record_recovered_declarations(recovery, |visitor| {
3677            visitor.visit_container(
3678                root,
3679                &scope.package_name,
3680                scope.module.clone(),
3681                scope.class_unit.clone(),
3682                scope.template_signature.clone(),
3683                scope.visible_using_namespaces.clone(),
3684            );
3685        });
3686        if end > node.end_byte() {
3687            self.consumed_fragment_regions
3688                .push((node.start_byte(), end));
3689        } else if node.kind() == "ERROR" && node.end_byte() > end {
3690            // The sentinel region ended at the first recovered class-like item
3691            // but the ERROR envelope keeps real sibling declarations after it
3692            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
3693            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
3694            // envelope's remaining children normally; the consumed region
3695            // keeps the reparsed prefix from being indexed twice.
3696            self.consumed_fragment_regions
3697                .push((node.start_byte(), end));
3698            stack.push(CppWork::Container(CppContainer {
3699                node,
3700                scope: scope.clone(),
3701            }));
3702        }
3703        true
3704    }
3705
3706    /// Re-own complete class declarations from the structured Abseil
3707    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
3708    /// its direct CST children already prove both namespace components and the
3709    /// class bodies, so the ordinary class/member visitor can retain ownership
3710    /// and exact source ranges without admitting unrelated callable bodies.
3711    fn visit_nested_namespace_sentinel<'tree>(
3712        &mut self,
3713        node: Node<'tree>,
3714        scope: &ScopeInfo,
3715        ancestry: &ParentIndex<'tree>,
3716    ) -> bool {
3717        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
3718            return false;
3719        };
3720
3721        let mut package_name = scope.package_name.clone();
3722        let mut module = scope.module.clone();
3723        for component in recovered.namespace_components {
3724            package_name = if package_name.is_empty() {
3725                component
3726            } else {
3727                format!("{package_name}::{component}")
3728            };
3729            let namespace_module = CodeUnit::new_fq(
3730                self.file.clone(),
3731                CodeUnitType::Module,
3732                "",
3733                package_name.clone(),
3734                cpp_namespace_fq(&package_name),
3735            );
3736            if !self.parsed.contains_declaration(&namespace_module) {
3737                self.parsed.add_code_unit(
3738                    namespace_module.clone(),
3739                    recovered.function,
3740                    self.source,
3741                    None,
3742                    None,
3743                );
3744            }
3745            module = Some(namespace_module);
3746        }
3747
3748        let recovered_scope = ScopeInfo {
3749            package_name,
3750            module,
3751            class_unit: scope.class_unit.clone(),
3752            template_signature: scope.template_signature.clone(),
3753            template_metadata: scope.template_metadata.clone(),
3754            declarations_are_fields: false,
3755            recovered_specialization_member_scope: false,
3756            visible_using_namespaces: scope.visible_using_namespaces.clone(),
3757        };
3758        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
3759            recovered.function,
3760            recovered.body,
3761            self.source,
3762            ancestry,
3763        ) {
3764            let mut class_scope = recovered_scope.clone();
3765            if let Some(template_node) = fragmented.template_node {
3766                class_scope.template_signature =
3767                    cpp_template_signature(template_node, fragmented.class_node, self.source);
3768                class_scope.template_metadata = cpp_template_metadata(
3769                    template_node,
3770                    fragmented.class_node,
3771                    self.source,
3772                    ancestry,
3773                );
3774            }
3775            if let Some(outcome) = self
3776                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
3777            {
3778                let mut class_stack = Vec::new();
3779                let class_unit = self.visit_named_class_like_shape(
3780                    fragmented.class_node,
3781                    fragmented.name.clone(),
3782                    None,
3783                    true,
3784                    Some(fragmented.fragmented.class_range),
3785                    fragmented.raw_supertypes.clone(),
3786                    &class_scope,
3787                    &mut class_stack,
3788                    ancestry,
3789                );
3790                self.parsed
3791                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
3792                        recovery: fragmented.fragmented.class_range,
3793                        unit: class_unit.clone(),
3794                    });
3795                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
3796                    self.consumed_fragment_regions.push((
3797                        fragmented.consumed_start,
3798                        fragmented.fragmented.class_range.end_byte,
3799                    ));
3800                }
3801            }
3802        }
3803        // The class requirement above is the admission gate; once admitted,
3804        // traverse the whole proven inner namespace body so sibling aliases,
3805        // functions, and variables are not silently discarded.
3806        self.run_container_work(recovered.body, recovered_scope);
3807        true
3808    }
3809
3810    fn visit_declaration<'tree>(
3811        &mut self,
3812        node: Node<'tree>,
3813        scope: &ScopeInfo,
3814        in_class_body: bool,
3815        stack: &mut Vec<CppWork<'tree>>,
3816        ancestry: &ParentIndex<'tree>,
3817    ) {
3818        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
3819            return;
3820        }
3821        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
3822            !cpp_active_template_type_parameter(
3823                node,
3824                node_text(declarator, self.source),
3825                self.source,
3826                ancestry,
3827            )
3828        }) {
3829            return;
3830        }
3831        if in_class_body
3832            && let Some(parent) = scope.class_unit.as_ref()
3833            && let Some(call) =
3834                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
3835        {
3836            self.visit_recovered_macro_qualified_constructor_definition(
3837                node, call, scope, ancestry,
3838            );
3839            return;
3840        }
3841        if in_class_body
3842            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
3843        {
3844            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
3845            return;
3846        }
3847        if in_class_body
3848            && let Some(declarators) =
3849                recovered_macro_qualified_field_declarators(node, self.source)
3850        {
3851            for declarator in declarators {
3852                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
3853            }
3854            return;
3855        }
3856        let recovered_alias_names = recovered_type_alias_names(node, self.source);
3857        if !recovered_alias_names.is_empty() {
3858            self.add_type_aliases(node, scope, recovered_alias_names);
3859            return;
3860        }
3861        if self.visit_c_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry)
3862        {
3863            return;
3864        }
3865
3866        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
3867            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
3868                // Issue #938: the members tree-sitter scattered out of the fragmented
3869                // multiple-base export node are reparsed from their true body region
3870                // and re-owned as members of the recovered class, with an explicit
3871                // navigation range spanning to the displaced closing brace.
3872                if let Some(outcome) =
3873                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
3874                {
3875                    let consumed_region = (
3876                        recovered.declaration_node.end_byte(),
3877                        fragmented.class_range.end_byte,
3878                    );
3879                    let code_unit = self.visit_named_class_like_shape(
3880                        recovered.declaration_node,
3881                        recovered.name,
3882                        None,
3883                        true,
3884                        Some(fragmented.class_range),
3885                        recovered.raw_supertypes,
3886                        scope,
3887                        stack,
3888                        ancestry,
3889                    );
3890                    self.parsed.record_materialization(
3891                        MaterializationRecord::RecoveredDeclaration {
3892                            recovery: fragmented.class_range,
3893                            unit: code_unit.clone(),
3894                        },
3895                    );
3896                    let consume_fragment =
3897                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
3898                    // Everything between the fragmented declaration and its displaced
3899                    // closing brace now belongs to the recovered class; keep the
3900                    // ordinary walk from re-indexing those scattered siblings at top
3901                    // level. Register the consumed region only after indexing because
3902                    // the reparsed nodes retain byte offsets inside that same region.
3903                    if consume_fragment {
3904                        self.consumed_fragment_regions.push(consumed_region);
3905                    }
3906                    return;
3907                }
3908            }
3909            let uses_initializer_body = recovered.uses_initializer_body;
3910            let definition_body_present = recovered.body.is_some();
3911            let class_unit = self.visit_named_class_like_shape(
3912                recovered.declaration_node,
3913                recovered.name,
3914                recovered.body,
3915                definition_body_present,
3916                None,
3917                recovered.raw_supertypes,
3918                scope,
3919                stack,
3920                ancestry,
3921            );
3922            self.parsed
3923                .record_materialization(MaterializationRecord::RecoveredDeclaration {
3924                    recovery: cpp_declaration_range(node),
3925                    unit: class_unit,
3926                });
3927            if uses_initializer_body {
3928                return;
3929            }
3930        }
3931
3932        let mut handled_function = false;
3933        let mut handled_declarator = false;
3934        let mut cursor = node.walk();
3935        for child in node.named_children(&mut cursor) {
3936            if matches!(
3937                child.kind(),
3938                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
3939            ) {
3940                // A named class-like definition remains a declaration even when
3941                // the same statement also declares an object, for example
3942                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
3943                // declaration's type and `kind` as its declarator.  Dropping the
3944                // type here loses both its nested owner and every later lexical
3945                // reference to it.  A body is the structured proof that this is
3946                // a definition rather than an elaborated type use such as
3947                // `class Kind value;`.
3948                if cpp_body_node(child).is_some() {
3949                    self.visit_class_like(child, scope, stack, ancestry);
3950                }
3951                continue;
3952            }
3953        }
3954
3955        let mut cursor = node.walk();
3956        for child in node.children_by_field_name("declarator", &mut cursor) {
3957            if crate::structural::is_recovered_designator_init_declarator(child) {
3958                handled_declarator = true;
3959                continue;
3960            }
3961            if let Some(kind) = classify_declarator(child) {
3962                handled_declarator = true;
3963                match kind {
3964                    DeclaratorKind::Function(function_declarator) => {
3965                        handled_function = true;
3966                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
3967                    }
3968                    DeclaratorKind::Variable(variable_declarator) => {
3969                        self.visit_variable_declaration(
3970                            node,
3971                            variable_declarator,
3972                            scope,
3973                            in_class_body,
3974                            ancestry,
3975                        );
3976                    }
3977                }
3978            }
3979        }
3980
3981        if !handled_declarator {
3982            let mut cursor = node.walk();
3983            for child in node.named_children(&mut cursor) {
3984                if crate::structural::is_recovered_designator_init_declarator(child) {
3985                    handled_declarator = true;
3986                    continue;
3987                }
3988                if !is_unfielded_declarator_candidate(child) {
3989                    continue;
3990                }
3991                let Some(kind) = classify_declarator(child) else {
3992                    continue;
3993                };
3994                handled_declarator = true;
3995                match kind {
3996                    DeclaratorKind::Function(function_declarator) => {
3997                        handled_function = true;
3998                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
3999                    }
4000                    DeclaratorKind::Variable(variable_declarator) => {
4001                        self.visit_variable_declaration(
4002                            node,
4003                            variable_declarator,
4004                            scope,
4005                            in_class_body,
4006                            ancestry,
4007                        );
4008                    }
4009                }
4010            }
4011        }
4012
4013        if handled_function {
4014            return;
4015        }
4016
4017        if !handled_declarator {
4018            if in_class_body {
4019                self.visit_class_members_from_declaration(node, scope, ancestry);
4020            } else {
4021                self.visit_global_variables_from_declaration(node, scope, ancestry);
4022            }
4023        }
4024    }
4025
4026    /// Preserve the member structure of an anonymous C aggregate.
4027    ///
4028    /// An anonymous union with no declarator promotes its fields into the
4029    /// containing aggregate. An anonymous struct/union followed by a named
4030    /// declarator, such as `struct { T *ops; } sock`, declares both the field
4031    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
4032    /// the declarator's structured nested identity so a later `value.sock.ops`
4033    /// chain can traverse it without parsing a type spelling (#2407).
4034    fn visit_c_anonymous_aggregate_declaration<'tree>(
4035        &mut self,
4036        node: Node<'tree>,
4037        scope: &ScopeInfo,
4038        in_class_body: bool,
4039        stack: &mut Vec<CppWork<'tree>>,
4040        ancestry: &ParentIndex<'tree>,
4041    ) -> bool {
4042        if !self.c_tag_semantics || !in_class_body || scope.class_unit.is_none() {
4043            return false;
4044        }
4045        let Some(aggregate) = node.child_by_field_name("type") else {
4046            return false;
4047        };
4048        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
4049            || aggregate.child_by_field_name("name").is_some()
4050        {
4051            return false;
4052        }
4053        let Some(body) = cpp_body_node(aggregate) else {
4054            return false;
4055        };
4056
4057        let mut cursor = node.walk();
4058        let declarators = node
4059            .children_by_field_name("declarator", &mut cursor)
4060            .filter_map(|declarator| match classify_declarator(declarator) {
4061                Some(DeclaratorKind::Variable(variable)) => Some(variable),
4062                Some(DeclaratorKind::Function(_)) | None => None,
4063            })
4064            .collect::<Vec<_>>();
4065        if declarators.is_empty() {
4066            stack.push(CppWork::Container(CppContainer {
4067                node: body,
4068                scope: scope.clone(),
4069            }));
4070            return true;
4071        }
4072
4073        for declarator in declarators {
4074            let Some(name) = extract_variable_name(declarator, self.source) else {
4075                continue;
4076            };
4077            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
4078            self.visit_named_class_like_shape(
4079                aggregate,
4080                name,
4081                Some(body),
4082                true,
4083                None,
4084                None,
4085                scope,
4086                stack,
4087                ancestry,
4088            );
4089        }
4090        true
4091    }
4092
4093    fn visit_function_declaration<'tree>(
4094        &mut self,
4095        declaration_node: Node<'tree>,
4096        declarator: Node<'tree>,
4097        scope: &ScopeInfo,
4098        ancestry: &ParentIndex<'tree>,
4099    ) {
4100        let Some(function) = extract_function_info(declarator, self.source, scope) else {
4101            return;
4102        };
4103        let code_unit =
4104            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
4105        if self.parsed.contains_declaration(&code_unit) {
4106            self.parsed
4107                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4108            return;
4109        }
4110        self.parsed
4111            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4112        let signature = render_cpp_function_display_signature_from_node(
4113            declaration_node,
4114            self.source,
4115            scope.template_signature.as_deref(),
4116            false,
4117            ancestry,
4118        );
4119        self.parsed.add_signature_with_metadata(
4120            code_unit.clone(),
4121            cpp_signature_metadata(signature, declarator, self.source, ancestry)
4122                .with_declaration_only(true)
4123                .with_callable_linkage(cpp_callable_linkage(
4124                    declaration_node,
4125                    self.source,
4126                    ancestry,
4127                )),
4128        );
4129        if let Some(parent) = &scope.class_unit {
4130            self.parsed.add_child(parent.clone(), code_unit);
4131        } else if let Some(module) = &scope.module {
4132            self.parsed.add_child(module.clone(), code_unit);
4133        }
4134    }
4135
4136    fn visit_recovered_macro_qualified_function_declaration<'tree>(
4137        &mut self,
4138        declaration_node: Node<'tree>,
4139        call: Node<'tree>,
4140        scope: &ScopeInfo,
4141        ancestry: &ParentIndex<'tree>,
4142    ) {
4143        let Some(parent) = &scope.class_unit else {
4144            return;
4145        };
4146        let Some(name_node) = call.child_by_field_name("function") else {
4147            return;
4148        };
4149        let Some(arguments) = call.child_by_field_name("arguments") else {
4150            return;
4151        };
4152        let Some((signature, parameter_labels)) =
4153            recovered_macro_qualified_function_parameters(arguments, self.source)
4154        else {
4155            return;
4156        };
4157        let arity = parameter_labels.len();
4158        let function = FunctionInfo {
4159            package_name: scope.package_name.clone(),
4160            owner: Some(CppMemberOwner::Unit(parent.clone())),
4161            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
4162            signature,
4163        };
4164        if function.name.is_empty() {
4165            return;
4166        }
4167        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4168        if self.parsed.contains_declaration(&code_unit) {
4169            self.parsed
4170                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
4171            return;
4172        }
4173        self.parsed
4174            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4175        let signature_label = render_cpp_function_display_signature_from_node(
4176            declaration_node,
4177            self.source,
4178            scope.template_signature.as_deref(),
4179            false,
4180            ancestry,
4181        );
4182        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4183            .with_declaration_only(true)
4184            .with_callable_arity(CallableArity::exact(arity))
4185            .with_callable_linkage(cpp_callable_linkage(
4186                declaration_node,
4187                self.source,
4188                ancestry,
4189            ));
4190        self.parsed
4191            .add_signature_with_metadata(code_unit.clone(), metadata);
4192        self.parsed.add_child(parent.clone(), code_unit);
4193    }
4194
4195    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
4196        &mut self,
4197        declaration_node: Node<'tree>,
4198        call: Node<'tree>,
4199        scope: &ScopeInfo,
4200        ancestry: &ParentIndex<'tree>,
4201    ) {
4202        let Some(parent) = &scope.class_unit else {
4203            return;
4204        };
4205        let Some(arguments) = call.child_by_field_name("arguments") else {
4206            return;
4207        };
4208        let Some((mut signature, parameter_labels)) =
4209            recovered_macro_qualified_function_parameters(arguments, self.source)
4210        else {
4211            return;
4212        };
4213        if let Some(template_signature) = &scope.template_signature {
4214            signature = format!("{template_signature}{signature}");
4215        }
4216        let arity = parameter_labels.len();
4217        let function = FunctionInfo {
4218            package_name: scope.package_name.clone(),
4219            owner: Some(CppMemberOwner::Unit(parent.clone())),
4220            name: parent.identifier().to_string(),
4221            signature,
4222        };
4223        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
4224        self.parsed
4225            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4226        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
4227        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
4228            .with_declaration_only(false)
4229            .with_callable_arity(CallableArity::exact(arity))
4230            .with_callable_linkage(cpp_callable_linkage(
4231                declaration_node,
4232                self.source,
4233                ancestry,
4234            ));
4235        self.parsed
4236            .add_signature_with_metadata(code_unit.clone(), metadata);
4237        self.parsed.add_child(parent.clone(), code_unit);
4238    }
4239
4240    fn visit_variable_declaration<'tree>(
4241        &mut self,
4242        declaration_node: Node<'tree>,
4243        declarator: Node<'tree>,
4244        scope: &ScopeInfo,
4245        in_class_body: bool,
4246        ancestry: &ParentIndex<'tree>,
4247    ) {
4248        let Some(name) = extract_variable_name(declarator, self.source) else {
4249            return;
4250        };
4251        let parent = if in_class_body {
4252            let Some(parent) = &scope.class_unit else {
4253                return;
4254            };
4255            Some(parent)
4256        } else {
4257            None
4258        };
4259        let short_name = match parent {
4260            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
4261            None => name.clone(),
4262        };
4263        let fq = cpp_leaf_fq(
4264            &scope.package_name,
4265            parent,
4266            &name,
4267            SegmentKind::Member,
4268            SegmentKind::Member,
4269        );
4270        let code_unit = CodeUnit::new_fq(
4271            self.file.clone(),
4272            CodeUnitType::Field,
4273            scope.package_name.clone(),
4274            short_name,
4275            fq,
4276        );
4277        if self.parsed.contains_declaration(&code_unit) {
4278            return;
4279        }
4280        self.parsed
4281            .add_code_unit(code_unit.clone(), declaration_node, self.source, None, None);
4282        self.parsed.add_signature_with_metadata(
4283            code_unit.clone(),
4284            SignatureMetadata::new(
4285                render_cpp_field_signature(declaration_node, declarator, self.source),
4286                Vec::new(),
4287            )
4288            .with_cpp_field_linkage(cpp_field_declaration_linkage(
4289                declaration_node,
4290                self.source,
4291                ancestry,
4292            )),
4293        );
4294        if let Some(parent) = &scope.class_unit {
4295            self.parsed.add_child(parent.clone(), code_unit);
4296        } else if let Some(module) = &scope.module {
4297            self.parsed.add_child(module.clone(), code_unit);
4298        }
4299    }
4300
4301    fn visit_class_members_from_declaration<'tree>(
4302        &mut self,
4303        node: Node<'tree>,
4304        scope: &ScopeInfo,
4305        ancestry: &ParentIndex<'tree>,
4306    ) {
4307        let mut cursor = node.walk();
4308        for child in node.named_children(&mut cursor) {
4309            if child.kind() == "init_declarator"
4310                && let Some(inner) = child.child_by_field_name("declarator")
4311            {
4312                self.visit_variable_declaration(node, inner, scope, true, ancestry);
4313            } else if matches!(
4314                child.kind(),
4315                "identifier"
4316                    | "field_identifier"
4317                    | "pointer_declarator"
4318                    | "reference_declarator"
4319                    | "array_declarator"
4320                    | "parenthesized_declarator"
4321            ) {
4322                self.visit_variable_declaration(node, child, scope, true, ancestry);
4323            }
4324        }
4325    }
4326
4327    fn visit_global_variables_from_declaration<'tree>(
4328        &mut self,
4329        node: Node<'tree>,
4330        scope: &ScopeInfo,
4331        ancestry: &ParentIndex<'tree>,
4332    ) {
4333        let mut cursor = node.walk();
4334        for child in node.named_children(&mut cursor) {
4335            if child.kind() == "init_declarator"
4336                && let Some(inner) = child.child_by_field_name("declarator")
4337            {
4338                self.visit_variable_declaration(node, inner, scope, false, ancestry);
4339            } else if matches!(
4340                child.kind(),
4341                "identifier"
4342                    | "field_identifier"
4343                    | "pointer_declarator"
4344                    | "reference_declarator"
4345                    | "array_declarator"
4346                    | "parenthesized_declarator"
4347            ) {
4348                self.visit_variable_declaration(node, child, scope, false, ancestry);
4349            }
4350        }
4351    }
4352
4353    fn visit_include(&mut self, node: Node<'_>) {
4354        let raw = normalize_cpp_whitespace(node_text(node, self.source));
4355        self.parsed.imports.push(ImportInfo {
4356            raw_snippet: raw,
4357            is_wildcard: false,
4358            is_global: false,
4359            identifier: None,
4360            alias: None,
4361            path: None,
4362            binder_span: None,
4363        });
4364    }
4365
4366    fn visit_type_declaration<'tree>(
4367        &mut self,
4368        node: Node<'tree>,
4369        scope: &ScopeInfo,
4370        stack: &mut Vec<CppWork<'tree>>,
4371        ancestry: &ParentIndex<'tree>,
4372    ) {
4373        let type_node = node.child_by_field_name("type");
4374        if let Some(type_node) = type_node
4375            && matches!(
4376                type_node.kind(),
4377                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
4378            )
4379        {
4380            self.visit_class_like(type_node, scope, stack, ancestry);
4381        }
4382
4383        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
4384            let range = Range {
4385                start_byte: node.start_byte(),
4386                end_byte: recovered.end_node.end_byte(),
4387                start_line: node.start_position().row + 1,
4388                end_line: recovered.end_node.end_position().row + 1,
4389            };
4390            let signature = self
4391                .source
4392                .get(range.start_byte..range.end_byte)
4393                .map(normalize_cpp_whitespace)
4394                .unwrap_or_default();
4395            self.record_type_aliases(node, scope, vec![recovered.name], signature, range);
4396            return;
4397        }
4398
4399        let alias_names = match node.kind() {
4400            "alias_declaration" => extract_alias_declaration_name(node, self.source)
4401                .into_iter()
4402                .collect::<Vec<_>>(),
4403            "type_definition" => extract_typedef_alias_names(node, self.source),
4404            _ => Vec::new(),
4405        };
4406        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
4407            (type_node, alias_names.as_slice())
4408            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
4409            && type_node.child_by_field_name("name").is_none()
4410        {
4411            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
4412        } else {
4413            None
4414        };
4415        self.add_type_aliases(node, scope, alias_names);
4416        if let Some((body, alias_name)) = anonymous_aggregate {
4417            // The typedef alias is also the only user-visible identity of an
4418            // anonymous aggregate. Reuse it as the member owner instead of
4419            // minting a second signatureless class with the same FQN. The
4420            // latter makes forward lookup ambiguous when conditional aliases
4421            // coexist and returns duplicate definitions even without guards.
4422            let signature = normalize_cpp_whitespace(node_text(node, self.source));
4423            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
4424            debug_assert!(self.parsed.contains_declaration(&alias_unit));
4425            let mut nested_scope = scope.clone();
4426            nested_scope.class_unit = Some(alias_unit);
4427            nested_scope.template_signature = scope.template_signature.clone();
4428            nested_scope.template_metadata = None;
4429            nested_scope.declarations_are_fields = false;
4430            nested_scope.recovered_specialization_member_scope = false;
4431            stack.push(CppWork::Container(CppContainer {
4432                node: body,
4433                scope: nested_scope,
4434            }));
4435        }
4436    }
4437
4438    fn add_type_aliases(&mut self, node: Node<'_>, scope: &ScopeInfo, alias_names: Vec<String>) {
4439        let signature = normalize_cpp_whitespace(node_text(node, self.source));
4440        self.record_type_aliases(
4441            node,
4442            scope,
4443            alias_names,
4444            signature,
4445            cpp_declaration_range(node),
4446        );
4447    }
4448
4449    fn record_type_aliases(
4450        &mut self,
4451        node: Node<'_>,
4452        scope: &ScopeInfo,
4453        alias_names: Vec<String>,
4454        signature: String,
4455        range: Range,
4456    ) {
4457        if signature.is_empty() {
4458            return;
4459        }
4460        let type_name = node
4461            .child_by_field_name("type")
4462            .and_then(|type_node| type_node.child_by_field_name("name"))
4463            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
4464        for alias_name in alias_names {
4465            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
4466                continue;
4467            }
4468            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
4469            // Declaration identity does not include the alias signature. Keep
4470            // each physical range so conditional aliases retain their guards.
4471            self.parsed
4472                .add_code_unit_with_range(code_unit.clone(), range, None, None);
4473            self.parsed
4474                .add_signature(code_unit.clone(), signature.clone());
4475            if let Some(metadata) = &scope.template_metadata {
4476                let mut metadata = metadata.clone();
4477                metadata.primary_fq_name = code_unit.fq_name();
4478                self.parsed
4479                    .set_cpp_template_metadata(code_unit.clone(), metadata);
4480            }
4481            if let Some(parent) = &scope.class_unit {
4482                self.parsed.add_child(parent.clone(), code_unit.clone());
4483            } else if let Some(module) = &scope.module {
4484                self.parsed.add_child(module.clone(), code_unit.clone());
4485            }
4486            self.parsed.mark_type_alias(code_unit);
4487        }
4488    }
4489
4490    fn type_alias_unit(
4491        &self,
4492        scope: &ScopeInfo,
4493        alias_name: String,
4494        signature: String,
4495    ) -> CodeUnit {
4496        let short_name = if let Some(parent) = &scope.class_unit {
4497            cpp_join_nested_short(parent.short_name(), &alias_name)
4498        } else {
4499            alias_name.clone()
4500        };
4501        let fq = cpp_leaf_fq(
4502            &scope.package_name,
4503            scope.class_unit.as_ref(),
4504            &alias_name,
4505            SegmentKind::Nested,
4506            SegmentKind::Type,
4507        );
4508        CodeUnit::with_signature_and_fq(
4509            self.file.clone(),
4510            CodeUnitType::Class,
4511            scope.package_name.clone(),
4512            short_name,
4513            Some(signature),
4514            false,
4515            fq,
4516        )
4517    }
4518
4519    fn visit_macro(&mut self, node: Node<'_>) {
4520        let Some(name) = extract_macro_name(node, self.source) else {
4521            return;
4522        };
4523        let signature = node_text(node, self.source).trim_end().to_string();
4524        if signature.is_empty() {
4525            return;
4526        }
4527        let fq = cpp_member_fq("", &name);
4528        let code_unit = CodeUnit::new_fq(self.file.clone(), CodeUnitType::Macro, "", name, fq);
4529        if self.parsed.contains_declaration_identity(&code_unit) {
4530            return;
4531        }
4532        self.parsed
4533            .add_code_unit(code_unit.clone(), node, self.source, None, None);
4534        let name_range = node
4535            .child_by_field_name("name")
4536            .map(cpp_declaration_range)
4537            .unwrap_or_else(|| cpp_declaration_range(node));
4538        self.parsed
4539            .record_materialization(MaterializationRecord::GeneratedDeclaration {
4540                site: cpp_declaration_range(node),
4541                argument: name_range,
4542                kind: GenerationKind::PreprocessorDefinition,
4543                unit: code_unit.clone(),
4544            });
4545        self.parsed.add_signature(code_unit, signature);
4546    }
4547}
4548
4549/// Classify a C++ field while its declaration syntax is already available.
4550///
4551/// The persisted result lets later visibility queries avoid reparsing the
4552/// complete source file only to recover linkage.
4553pub fn cpp_field_declaration_linkage<'tree>(
4554    declaration: Node<'tree>,
4555    source: &str,
4556    ancestry: &ParentIndex<'tree>,
4557) -> CppFieldLinkage {
4558    let mut current = ancestry.parent(declaration);
4559    let mut enclosed_by_class = false;
4560    while let Some(node) = current {
4561        if node.kind() == "namespace_definition"
4562            && node
4563                .child_by_field_name("name")
4564                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4565        {
4566            return CppFieldLinkage::Internal;
4567        }
4568        if matches!(
4569            node.kind(),
4570            "class_specifier" | "struct_specifier" | "union_specifier"
4571        ) && node
4572            .child_by_field_name("name")
4573            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
4574        {
4575            return CppFieldLinkage::Internal;
4576        }
4577        if matches!(
4578            node.kind(),
4579            "class_specifier" | "struct_specifier" | "union_specifier"
4580        ) {
4581            enclosed_by_class = true;
4582        }
4583        if matches!(node.kind(), "function_definition" | "lambda_expression") {
4584            return CppFieldLinkage::Internal;
4585        }
4586        current = ancestry.parent(node);
4587    }
4588    if enclosed_by_class {
4589        return CppFieldLinkage::External;
4590    }
4591    let mut cursor = declaration.walk();
4592    let mut has_static = false;
4593    let mut has_extern = false;
4594    let mut has_inline = false;
4595    let mut has_const = false;
4596    let mut has_constexpr = false;
4597    for child in declaration.named_children(&mut cursor) {
4598        let text = normalize_cpp_whitespace(node_text(child, source));
4599        match (child.kind(), text.as_str()) {
4600            ("storage_class_specifier", "static") => has_static = true,
4601            ("storage_class_specifier", "extern") => has_extern = true,
4602            ("storage_class_specifier", "inline") => has_inline = true,
4603            ("storage_class_specifier", "constexpr") => has_constexpr = true,
4604            ("type_qualifier", "const") => has_const = true,
4605            ("type_qualifier", "constexpr") => has_constexpr = true,
4606            _ => {}
4607        }
4608    }
4609    if has_static {
4610        CppFieldLinkage::Internal
4611    } else if has_extern || has_inline {
4612        CppFieldLinkage::External
4613    } else if has_const || has_constexpr {
4614        CppFieldLinkage::InternalUnlessExternalPeer
4615    } else {
4616        CppFieldLinkage::External
4617    }
4618}
4619
4620fn cpp_declaration_range(node: Node<'_>) -> Range {
4621    Range {
4622        start_byte: node.start_byte(),
4623        end_byte: node.end_byte(),
4624        start_line: node.start_position().row + 1,
4625        end_line: node.end_position().row + 1,
4626    }
4627}
4628
4629/// A recovery interval as a [`Range`], for materialization records whose
4630/// window is a byte region rather than one parser node (the sentinel-macro
4631/// region reparses, issue #941/#1657).
4632fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
4633    let line_at = |byte: usize| {
4634        source.as_bytes()[..byte]
4635            .iter()
4636            .filter(|&&b| b == b'\n')
4637            .count()
4638            + 1
4639    };
4640    Range {
4641        start_byte,
4642        end_byte,
4643        start_line: line_at(start_byte),
4644        end_line: line_at(end_byte),
4645    }
4646}
4647
4648pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
4649    let mut in_block_comment = false;
4650    for line in source.lines() {
4651        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
4652        let trimmed = stripped.trim();
4653        if !looks_like_quoted_include_line(trimmed) {
4654            continue;
4655        }
4656
4657        let raw = normalize_cpp_whitespace(trimmed);
4658        // The tree-sitter walk already recorded every `#include` it could see;
4659        // this line scan only recovers the ones a parse error hid, so skip a
4660        // snippet that is already an import binding.
4661        if parsed
4662            .imports
4663            .iter()
4664            .any(|import| import.raw_snippet == raw)
4665        {
4666            continue;
4667        }
4668
4669        parsed.imports.push(ImportInfo {
4670            raw_snippet: raw,
4671            is_wildcard: false,
4672            is_global: false,
4673            identifier: None,
4674            alias: None,
4675            path: None,
4676            binder_span: None,
4677        });
4678    }
4679}
4680
4681fn looks_like_quoted_include_line(line: &str) -> bool {
4682    let Some(rest) = line.trim_start().strip_prefix('#') else {
4683        return false;
4684    };
4685    let Some(rest) = rest.trim_start().strip_prefix("include") else {
4686        return false;
4687    };
4688    rest.trim_start().starts_with('"')
4689}
4690
4691fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
4692    let mut raw = Vec::new();
4693    let mut cursor = node.walk();
4694    for child in node.named_children(&mut cursor) {
4695        if child.kind() == "base_class_clause" {
4696            collect_cpp_base_nodes(child, source, &mut raw);
4697        }
4698    }
4699    raw
4700}
4701
4702fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
4703    walk_named_tree_preorder(node, false, |child| match child.kind() {
4704        "type_identifier" | "qualified_identifier" | "template_type" => {
4705            let text = normalize_cpp_whitespace(node_text(child, source));
4706            if !text.is_empty() {
4707                raw.push(text);
4708            }
4709            WalkControl::SkipChildren
4710        }
4711        _ => WalkControl::Continue,
4712    });
4713}
4714
4715fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
4716    let mut out = String::new();
4717    let chars: Vec<char> = line.chars().collect();
4718    let mut index = 0;
4719    let mut in_string = false;
4720    let mut in_char = false;
4721    let mut escape = false;
4722
4723    while index < chars.len() {
4724        let ch = chars[index];
4725        let next = chars.get(index + 1).copied();
4726
4727        if *in_block_comment {
4728            if ch == '*' && next == Some('/') {
4729                *in_block_comment = false;
4730                index += 2;
4731            } else {
4732                index += 1;
4733            }
4734            continue;
4735        }
4736
4737        if in_string {
4738            out.push(ch);
4739            if escape {
4740                escape = false;
4741            } else if ch == '\\' {
4742                escape = true;
4743            } else if ch == '"' {
4744                in_string = false;
4745            }
4746            index += 1;
4747            continue;
4748        }
4749
4750        if in_char {
4751            out.push(ch);
4752            if escape {
4753                escape = false;
4754            } else if ch == '\\' {
4755                escape = true;
4756            } else if ch == '\'' {
4757                in_char = false;
4758            }
4759            index += 1;
4760            continue;
4761        }
4762
4763        if ch == '/' && next == Some('/') {
4764            break;
4765        }
4766        if ch == '/' && next == Some('*') {
4767            *in_block_comment = true;
4768            index += 2;
4769            continue;
4770        }
4771        if ch == '"' {
4772            in_string = true;
4773            out.push(ch);
4774            index += 1;
4775            continue;
4776        }
4777        if ch == '\'' {
4778            in_char = true;
4779            out.push(ch);
4780            index += 1;
4781            continue;
4782        }
4783
4784        out.push(ch);
4785        index += 1;
4786    }
4787
4788    out
4789}
4790
4791#[derive(Clone)]
4792struct FunctionInfo {
4793    package_name: String,
4794    owner: Option<CppMemberOwner>,
4795    name: String,
4796    signature: String,
4797}
4798
4799/// Owner of a member function, kept structured so a literal `$` inside a
4800/// source-level class name never crosses a join/split boundary: the legacy
4801/// `$`-joined owner string was re-split at fq construction, dropping a leading
4802/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
4803/// and tripping the package/short boundary assert -- the #2140 corruption one
4804/// level up (#2362).
4805#[derive(Clone)]
4806enum CppMemberOwner {
4807    /// Source-level owner class chain from a qualified declarator-id, one
4808    /// class name per component (`Outer::Inner::method` -> `["Outer",
4809    /// "Inner"]`); each component may itself contain a literal `$`.
4810    Chain(Vec<String>),
4811    /// The lexically enclosing or recovered class unit; the member fq extends
4812    /// its fq directly instead of re-splitting its `$`-joined short chain.
4813    Unit(CodeUnit),
4814}
4815
4816impl CppMemberOwner {
4817    /// The legacy `$`-joined owner chain used in the member's short name.
4818    fn short_chain(&self) -> String {
4819        match self {
4820            Self::Chain(chain) => chain.join("$"),
4821            Self::Unit(parent) => parent.short_name().to_string(),
4822        }
4823    }
4824}
4825
4826enum DeclaratorKind<'a> {
4827    Function(Node<'a>),
4828    Variable(Node<'a>),
4829}
4830
4831impl FunctionInfo {
4832    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
4833        self.code_unit_with_synthetic(file, false)
4834    }
4835
4836    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
4837        let short_name = match &self.owner {
4838            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
4839            None => self.name.clone(),
4840        };
4841        let fq = match &self.owner {
4842            Some(CppMemberOwner::Chain(chain)) => {
4843                debug_assert!(
4844                    !chain.is_empty(),
4845                    "an empty owner chain is no owner; producers return None instead"
4846                );
4847                let mut fq = FqName::new();
4848                cpp_push_package(&mut fq, &self.package_name);
4849                let mut first = true;
4850                for component in chain {
4851                    let kind = if first {
4852                        SegmentKind::Type
4853                    } else {
4854                        SegmentKind::Nested
4855                    };
4856                    fq.push(cpp_segment(component, kind));
4857                    first = false;
4858                }
4859                fq.push(cpp_segment(&self.name, SegmentKind::Member));
4860                fq
4861            }
4862            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
4863                .fq()
4864                .clone()
4865                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
4866            // An anonymous parent (empty short chain) contributes no owner
4867            // segment -- the same guard as cpp_join_member_short above.
4868            Some(CppMemberOwner::Unit(_)) | None => {
4869                let mut fq = FqName::new();
4870                cpp_push_package(&mut fq, &self.package_name);
4871                fq.push(cpp_segment(&self.name, SegmentKind::Member));
4872                fq
4873            }
4874        };
4875        CodeUnit::with_signature_and_fq(
4876            file,
4877            CodeUnitType::Function,
4878            self.package_name.clone(),
4879            short_name,
4880            Some(self.signature.clone()),
4881            synthetic,
4882            fq,
4883        )
4884    }
4885}
4886
4887fn extract_function_info(
4888    declarator: Node<'_>,
4889    source: &str,
4890    scope: &ScopeInfo,
4891) -> Option<FunctionInfo> {
4892    let parameters_node = declarator.child_by_field_name("parameters")?;
4893    let declarator_name_node = declarator
4894        .child_by_field_name("declarator")
4895        .or_else(|| parameters_node.prev_named_sibling())?;
4896    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
4897}
4898
4899fn extract_function_info_from_name(
4900    declarator: Node<'_>,
4901    declarator_name_node: Node<'_>,
4902    source: &str,
4903    scope: &ScopeInfo,
4904) -> Option<FunctionInfo> {
4905    let parameters_node = declarator.child_by_field_name("parameters")?;
4906    let parameters_text = cpp_parameter_signature(parameters_node, source);
4907    let recovered_specialization_member = scope
4908        .recovered_specialization_member_scope
4909        .then(|| {
4910            let terminal = declarator_name_node
4911                .child_by_field_name("name")
4912                .unwrap_or(declarator_name_node);
4913            let name = canonical_cpp_qualified_component(terminal, source)?.name;
4914            let owner = scope.class_unit.as_ref()?;
4915            Some((
4916                Some(CppMemberOwner::Unit(owner.clone())),
4917                name,
4918                scope.package_name.clone(),
4919            ))
4920        })
4921        .flatten();
4922    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
4923        parts
4924    } else if let Some(parts) =
4925        split_structured_templated_cpp_name(declarator_name_node, source, scope)
4926    {
4927        parts
4928    } else {
4929        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
4930            declarator_name_node,
4931            source,
4932        )?);
4933        if raw_name.is_empty() {
4934            return None;
4935        }
4936        split_cpp_name(&raw_name, scope)
4937    };
4938    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
4939    let mut signature = if suffix.is_empty() {
4940        parameters_text
4941    } else {
4942        format!("{parameters_text} {suffix}")
4943    };
4944    if let Some(template_signature) = &scope.template_signature {
4945        signature = format!("{template_signature}{signature}");
4946    }
4947
4948    Some(FunctionInfo {
4949        package_name,
4950        owner,
4951        name,
4952        signature,
4953    })
4954}
4955
4956/// Recover the semantic return type and callable name when a declaration macro
4957/// occupies a function definition's `type` field. Tree-sitter either exposes a
4958/// scalar return as the declarator's apparent name and the callable as the sole
4959/// identifier in an `ERROR`, or joins a template return and callable into a
4960/// qualified identifier with a missing `::`. Both shapes retain the complete
4961/// parameter list and body; a concrete separator remains an out-of-line member.
4962fn cpp_macro_displaced_callable_parts<'tree>(
4963    function_declarator: Node<'tree>,
4964    source: &str,
4965    ancestry: &ParentIndex<'tree>,
4966) -> Option<(Node<'tree>, Node<'tree>)> {
4967    let definition = ancestry.parent(function_declarator)?;
4968    if definition.kind() != "function_definition"
4969        || definition.child_by_field_name("declarator") != Some(function_declarator)
4970        || definition
4971            .child_by_field_name("body")
4972            .is_none_or(|body| body.kind() != "compound_statement")
4973    {
4974        return None;
4975    }
4976    let macro_type = definition.child_by_field_name("type")?;
4977    if macro_type.kind() != "type_identifier"
4978        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
4979    {
4980        return None;
4981    }
4982
4983    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
4984    if apparent_return_type.kind() == "qualified_identifier"
4985        && let (Some(return_type), Some(callable_name)) = (
4986            apparent_return_type.child_by_field_name("scope"),
4987            apparent_return_type.child_by_field_name("name"),
4988        )
4989        && return_type.kind() == "template_type"
4990        && matches!(callable_name.kind(), "identifier" | "field_identifier")
4991        && (0..apparent_return_type.child_count())
4992            .filter_map(|index| apparent_return_type.child(index))
4993            .any(|child| child.kind() == "::" && child.is_missing())
4994        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
4995        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
4996    {
4997        return Some((return_type, callable_name));
4998    }
4999    if !matches!(
5000        apparent_return_type.kind(),
5001        "identifier" | "field_identifier" | "type_identifier"
5002    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
5003    {
5004        return None;
5005    }
5006    let parameters = function_declarator.child_by_field_name("parameters")?;
5007    let mut cursor = function_declarator.walk();
5008    let between = function_declarator
5009        .named_children(&mut cursor)
5010        .filter(|child| child.kind() != "comment")
5011        .filter(|child| {
5012            child.start_byte() >= apparent_return_type.end_byte()
5013                && child.end_byte() <= parameters.start_byte()
5014                && !same_node(*child, apparent_return_type)
5015                && !same_node(*child, parameters)
5016        })
5017        .collect::<Vec<_>>();
5018    let [name_error] = between.as_slice() else {
5019        return None;
5020    };
5021    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
5022        return None;
5023    }
5024    let callable_name = name_error.named_child(0)?;
5025    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
5026        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
5027    {
5028        return None;
5029    }
5030    Some((apparent_return_type, callable_name))
5031}
5032
5033/// The part of a `function_declarator` after its parameter list that belongs to
5034/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
5035/// specification, a trailing return type and a trailing requires-clause.
5036///
5037/// The grammar makes each of these a distinct sibling of the `parameters`
5038/// field, so they are read from the tree. Splitting the declarator's text on
5039/// the parameter list instead silently dropped every qualifier whenever the
5040/// parameter list was spelled with whitespace that normalization rewrote - a
5041/// line break or a double space was enough to make a `const` member definition
5042/// a different logical symbol from its declaration (#1827).
5043///
5044/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
5045/// are deliberately excluded. C++ does not make them part of the signature and
5046/// an out-of-line definition never repeats them, so including them would split
5047/// a declaration from its own definition.
5048fn cpp_declarator_identity_suffix(
5049    declarator: Node<'_>,
5050    parameters_node: Node<'_>,
5051    source: &str,
5052) -> String {
5053    let mut cursor = declarator.walk();
5054    let parts = declarator
5055        .named_children(&mut cursor)
5056        .filter(|child| child.start_byte() >= parameters_node.end_byte())
5057        .filter(|child| {
5058            matches!(
5059                child.kind(),
5060                "type_qualifier"
5061                    | "ref_qualifier"
5062                    | "noexcept"
5063                    | "throw_specifier"
5064                    | "trailing_return_type"
5065                    | "requires_clause"
5066            )
5067        })
5068        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
5069        .filter(|text| !text.is_empty())
5070        .collect::<Vec<_>>();
5071    normalize_cpp_qualifier_suffix(&parts.join(" "))
5072}
5073
5074/// The identity suffix of one callable declarator, for a consumer that holds
5075/// the declarator rather than the declaration walk's parts.
5076///
5077/// The persisted signature concatenates the parameter spelling and this suffix,
5078/// so a comparison that must agree on the suffix alone recomputes it here
5079/// instead of splitting the stored string.
5080pub(crate) fn cpp_callable_identity_suffix(
5081    function_declarator: Node<'_>,
5082    source: &str,
5083) -> Option<String> {
5084    let parameters_node = function_declarator.child_by_field_name("parameters")?;
5085    Some(cpp_declarator_identity_suffix(
5086        function_declarator,
5087        parameters_node,
5088        source,
5089    ))
5090}
5091
5092fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
5093    match classify_declarator(node)? {
5094        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
5095        DeclaratorKind::Variable(_) => None,
5096    }
5097}
5098
5099fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
5100    match node.kind() {
5101        "function_declarator" => {
5102            let inner = node
5103                .child_by_field_name("declarator")
5104                .or_else(|| node.child_by_field_name("name"))
5105                .or_else(|| last_named_child(node));
5106            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
5107                Some(DeclaratorKind::Variable(node))
5108            } else {
5109                Some(DeclaratorKind::Function(node))
5110            }
5111        }
5112        "init_declarator"
5113        | "pointer_declarator"
5114        | "reference_declarator"
5115        | "parenthesized_declarator"
5116        | "array_declarator"
5117        | "attributed_declarator"
5118        | "template_function" => node
5119            .child_by_field_name("declarator")
5120            .or_else(|| node.child_by_field_name("name"))
5121            .or_else(|| last_named_child(node))
5122            .and_then(classify_declarator),
5123        "identifier" | "field_identifier" | "qualified_identifier" => {
5124            Some(DeclaratorKind::Variable(node))
5125        }
5126        _ => node
5127            .child_by_field_name("declarator")
5128            .or_else(|| node.child_by_field_name("name"))
5129            .or_else(|| last_named_child(node))
5130            .and_then(classify_declarator),
5131    }
5132}
5133
5134fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
5135    matches!(
5136        node.kind(),
5137        "function_declarator"
5138            | "init_declarator"
5139            | "pointer_declarator"
5140            | "reference_declarator"
5141            | "parenthesized_declarator"
5142            | "array_declarator"
5143            | "attributed_declarator"
5144            | "template_function"
5145            | "identifier"
5146            | "field_identifier"
5147            | "qualified_identifier"
5148    )
5149}
5150
5151fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
5152    let class_like = first_class_like_child(node);
5153    let mut cursor = node.walk();
5154    node.named_children(&mut cursor).any(|child| {
5155        matches!(
5156            child.kind(),
5157            "init_declarator"
5158                | "pointer_declarator"
5159                | "reference_declarator"
5160                | "array_declarator"
5161                | "function_declarator"
5162                | "parenthesized_declarator"
5163                | "attributed_declarator"
5164        ) || matches!(
5165            child.kind(),
5166            "identifier" | "field_identifier" | "qualified_identifier"
5167        ) && class_like.is_none_or(|class_node| {
5168            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
5169        })
5170    })
5171}
5172
5173/// Find the unique namespace-scope forward declaration that precedes a
5174/// recovered export-macro class definition.  Tree-sitter can close a malformed
5175/// class at the enclosing namespace's closing brace, leaving the later class
5176/// definitions as root-level recovered `function_definition` nodes.  A
5177/// preceding `class Name;` in the same namespace is the only structured identity
5178/// signal available in that shape.
5179///
5180/// The search is deliberately conservative: it only accepts a body-less class
5181/// specifier whose declaration has no declarator and is not nested in a function
5182/// or class body.  More than one matching namespace forward declaration is
5183/// ambiguous and returns `None` rather than guessing.
5184fn unique_earlier_cpp_namespace_forward<'tree>(
5185    recovered_node: Node<'tree>,
5186    name: &str,
5187    source: &str,
5188    ancestry: &ParentIndex<'tree>,
5189) -> Option<String> {
5190    let mut root = recovered_node;
5191    while let Some(parent) = ancestry.parent(root) {
5192        root = parent;
5193    }
5194
5195    let mut candidates = Vec::new();
5196    let mut stack = vec![root];
5197    while let Some(current) = stack.pop() {
5198        if current.start_byte() < recovered_node.start_byte()
5199            && matches!(
5200                current.kind(),
5201                "class_specifier" | "struct_specifier" | "union_specifier"
5202            )
5203            && cpp_body_node(current).is_none()
5204            && current.parent().is_some_and(|parent| {
5205                parent.kind() == "declaration_list"
5206                    || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent)
5207            })
5208            && class_like_name(current, source, ancestry).as_deref() == Some(name)
5209            && cpp_namespace_definition_for_forward(current, ancestry).is_some_and(|namespace| {
5210                // Borrowing is only justified by the parser-recovery shape we
5211                // are repairing: the namespace that held the forward must
5212                // itself contain a syntax error and must have closed before
5213                // the root-level recovered class. A clean, unrelated
5214                // namespace forward is not an identity proof.
5215                namespace.has_error()
5216                    && namespace.end_byte() < recovered_node.start_byte()
5217                    && malformed_namespace_is_nearest_recovery_region(namespace, recovered_node)
5218            })
5219            && let Some(package_name) = cpp_namespace_name_for_forward(current, source, ancestry)
5220        {
5221            candidates.push(package_name);
5222        }
5223
5224        let mut cursor = current.walk();
5225        for child in current.named_children(&mut cursor) {
5226            if child.start_byte() < recovered_node.start_byte() {
5227                stack.push(child);
5228            }
5229        }
5230    }
5231
5232    if candidates.len() == 1 {
5233        candidates.pop()
5234    } else {
5235        None
5236    }
5237}
5238
5239fn malformed_namespace_is_nearest_recovery_region(
5240    namespace: Node<'_>,
5241    recovered_node: Node<'_>,
5242) -> bool {
5243    let mut root = recovered_node;
5244    while let Some(parent) = root.parent() {
5245        root = parent;
5246    }
5247    let mut cursor = root.walk();
5248    root.named_children(&mut cursor)
5249        .filter(|sibling| {
5250            namespace.end_byte() <= sibling.start_byte()
5251                && sibling.end_byte() <= recovered_node.start_byte()
5252        })
5253        .all(is_malformed_namespace_recovery_trivia)
5254}
5255
5256fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
5257    matches!(node.kind(), "ERROR" | "comment")
5258        || node.kind().starts_with("preproc_")
5259        || node.kind() == "expression_statement" && node.named_child_count() == 0
5260}
5261
5262/// Return the namespace path for a forward class only when the declaration is
5263/// at namespace scope.  A declaration nested in a function/class body may share
5264/// the same namespace ancestor but cannot identify a top-level class definition.
5265fn cpp_namespace_name_for_forward<'tree>(
5266    node: Node<'tree>,
5267    source: &str,
5268    ancestry: &ParentIndex<'tree>,
5269) -> Option<String> {
5270    cpp_namespace_definition_for_forward(node, ancestry)?;
5271    cpp_lexical_namespace_name(node, source, ancestry)
5272}
5273
5274fn cpp_namespace_definition_for_forward<'tree>(
5275    node: Node<'tree>,
5276    ancestry: &ParentIndex<'tree>,
5277) -> Option<Node<'tree>> {
5278    let declaration = ancestry.parent(node)?;
5279    let mut ancestor = ancestry.parent(declaration);
5280    while let Some(current) = ancestor {
5281        if matches!(
5282            current.kind(),
5283            "compound_statement"
5284                | "field_declaration_list"
5285                | "class_specifier"
5286                | "struct_specifier"
5287                | "union_specifier"
5288                | "function_definition"
5289                | "lambda_expression"
5290        ) {
5291            return None;
5292        }
5293        if current.kind() == "namespace_definition" {
5294            return Some(current);
5295        }
5296        ancestor = ancestry.parent(current);
5297    }
5298    None
5299}
5300
5301fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
5302    match node.kind() {
5303        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5304        "parenthesized_declarator" => node
5305            .child_by_field_name("declarator")
5306            .or_else(|| last_named_child(node))
5307            .is_some_and(is_pointer_wrapper_declarator),
5308        "template_function" => node
5309            .child_by_field_name("name")
5310            .is_some_and(is_function_pointer_like_inner_declarator),
5311        _ => false,
5312    }
5313}
5314
5315fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
5316    match node.kind() {
5317        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
5318        "parenthesized_declarator" => node
5319            .child_by_field_name("declarator")
5320            .or_else(|| last_named_child(node))
5321            .is_some_and(is_pointer_wrapper_declarator),
5322        _ => false,
5323    }
5324}
5325
5326fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
5327    let cleaned = raw_name.trim_start_matches("template ").trim();
5328    // A leading `::` is the explicit-global marker, not an empty owner segment.
5329    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
5330    // erroneous macro envelope swallowing the first identifier of an
5331    // out-of-line `X::X` constructor, chromium #1573); without this strip the
5332    // split below yields owner_parts `[""]`, constructing a unit with an empty
5333    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
5334    let cleaned = cleaned.trim_start_matches("::");
5335    // Parser recovery can preserve two adjacent scope operators around a
5336    // missing component (for example `X::/**/::method` in compiler diagnostic
5337    // fixtures). Empty components are syntax-recovery artifacts, never C++
5338    // owners. Keeping one as the final owner constructed `short_name=".method"`
5339    // and violated the structured package/short boundary during a large LLVM
5340    // workspace build. This is the same legacy-string-to-FqName bridge as the
5341    // ordinary split above; discard only components that the delimiter itself
5342    // proves empty.
5343    let parts: Vec<_> = cleaned
5344        .split("::")
5345        .filter(|component| !component.is_empty())
5346        .collect();
5347    if parts.is_empty() {
5348        return (None, cleaned.to_string(), scope.package_name.clone());
5349    }
5350    if parts.len() > 1 {
5351        let name = parts.last().unwrap_or(&cleaned).to_string();
5352        let owner_parts = &parts[..parts.len() - 1];
5353        if let Some(class_unit) = &scope.class_unit {
5354            // Lexically inside a class body: the owner is that class, whatever
5355            // the declarator re-qualifies it as.
5356            return (
5357                Some(CppMemberOwner::Unit(class_unit.clone())),
5358                name,
5359                scope.package_name.clone(),
5360            );
5361        }
5362        if !scope.package_name.is_empty() {
5363            // Out-of-line member definition written *inside* an enclosing
5364            // `namespace {}` block (scope package is that namespace). Every
5365            // owner segment before the terminal member is a class-nesting step
5366            // -- an out-of-line nested-class member `Outer::Inner::method` in
5367            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
5368            // namespace path: `using namespace` never brings nested-class
5369            // access into unqualified scope, so C++ always writes the full
5370            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
5371            // that redundantly re-states the enclosing namespace it already
5372            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
5373            // strip that re-qualifying prefix (which duplicates a suffix of the
5374            // enclosing package path) before treating what remains as the
5375            // nested-class chain, so the redundant spelling still lands on the
5376            // same `log4cxx.Foo.method` identity as its header declaration.
5377            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
5378            let owner = (!nested.is_empty()).then(|| {
5379                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
5380            });
5381            return (owner, name, scope.package_name.clone());
5382        }
5383        // File scope (no enclosing `namespace {}` block, scope package empty).
5384        let (owner, package_name) = if owner_parts.len() > 1 {
5385            // A multi-segment qualifier at file scope with no enclosing
5386            // namespace: treat all but the last owner segment as the namespace
5387            // path and the last as the owning class (`ns1::ns2::Class::method`
5388            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
5389            // is really a namespace or an outer class cannot be told from the
5390            // declarator text alone here, and no enclosing namespace or
5391            // in-index owner is available at per-file extraction to confirm the
5392            // class reading, so the far-more-common namespace interpretation is
5393            // kept rather than guessed away (the nested-class-at-file-scope and
5394            // using-directive-qualified nested-class shapes remain on this
5395            // behavior; see #1121).
5396            (
5397                Some(CppMemberOwner::Chain(vec![
5398                    owner_parts.last().unwrap_or(&"").to_string(),
5399                ])),
5400                owner_parts[..owner_parts.len() - 1].join("::"),
5401            )
5402        } else {
5403            // A bare `Class::member` qualifier at file scope carries no
5404            // namespace segment of its own. The declarator alone cannot say
5405            // which namespace owns `Class` -- but a `using namespace X;`
5406            // directive already in effect at this point in the file (#1093,
5407            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
5408            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
5409            // is the remaining structural signal for it, so fall back to it
5410            // rather than leaving the definition's package empty while its
5411            // header declaration (parsed inside the `namespace {}` block) keeps
5412            // the real one -- an identity split that made the same member
5413            // unresolvable under its own displayed spelling.
5414            (
5415                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
5416                cpp_using_directive_namespace_for_bare_owner(scope),
5417            )
5418        };
5419        return (owner, name, package_name);
5420    }
5421
5422    let package_name = scope.package_name.clone();
5423    let owner = scope
5424        .class_unit
5425        .as_ref()
5426        .map(|parent| CppMemberOwner::Unit(parent.clone()));
5427    (owner, cleaned.to_string(), package_name)
5428}
5429
5430/// Drop the leading owner segments of an out-of-line member qualifier that
5431/// merely re-state the enclosing namespace the definition already sits in, so
5432/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
5433/// definition may redundantly write `a::b::Outer::Inner::method` (or the
5434/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
5435/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
5436/// noise, not class-nesting steps. Returns the owner segments with the longest
5437/// such re-qualifying prefix removed (possibly all of them, when the qualifier
5438/// names only the enclosing namespace before the terminal member -- a
5439/// re-qualified free function). `package_name` is the enclosing namespace path
5440/// in its stored `::`-joined form; both sides are split on the same delimiter
5441/// the namespace walker joined them with, so this compares namespace *segments*
5442/// rather than scanning text.
5443fn strip_redundant_namespace_prefix<'a>(
5444    owner_parts: &'a [&'a str],
5445    package_name: &str,
5446) -> &'a [&'a str] {
5447    if package_name.is_empty() {
5448        return owner_parts;
5449    }
5450    let package_segments: Vec<&str> = package_name.split("::").collect();
5451    let max_prefix = owner_parts.len().min(package_segments.len());
5452    for prefix_len in (1..=max_prefix).rev() {
5453        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
5454        if &owner_parts[..prefix_len] == package_suffix {
5455            return &owner_parts[prefix_len..];
5456        }
5457    }
5458    owner_parts
5459}
5460
5461/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
5462/// class name at file/namespace scope, from the `using namespace` directives
5463/// visible at this point in the file. Several may be in scope at once (a
5464/// primary `using namespace NS;` alongside deeper conveniences like `using
5465/// namespace NS::helpers;`); since the declarator gives no way to tell which
5466/// one actually declares the owner class, prefer the shallowest (fewest
5467/// `::`-separated segments) as the file's most likely "home" namespace,
5468/// breaking ties by declaration order. Returns an empty string (leaving the
5469/// caller's package unqualified, as before) when no using-namespace directive
5470/// is in scope.
5471fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
5472    scope
5473        .visible_using_namespaces
5474        .iter()
5475        .min_by_key(|namespace| namespace.split("::").count())
5476        .cloned()
5477        .unwrap_or_default()
5478}
5479
5480struct CppQualifiedNameComponent {
5481    name: String,
5482    is_template_id: bool,
5483}
5484
5485/// Canonical nested-class chain for an out-of-line class definition written
5486/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
5487/// component per class (`["Outer", "Inner"]`).
5488///
5489/// The enclosing namespace fixes the namespace/class boundary: after an
5490/// optional redundant spelling of that namespace, every component belongs to
5491/// the class chain. File-scope qualified class names remain untouched because
5492/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
5493///
5494/// The components stay structured (rather than being `$`-joined here) so the
5495/// fq construction can push one Type/Nested segment per class; the `$`-joined
5496/// short-name display form is derived at the call sites that need it.
5497fn qualified_class_name_chain(
5498    class_node: Node<'_>,
5499    source: &str,
5500    scope: &ScopeInfo,
5501) -> Option<Vec<String>> {
5502    if scope.package_name.is_empty() || scope.class_unit.is_some() {
5503        return None;
5504    }
5505    let name = class_node.child_by_field_name("name")?;
5506    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
5507    if explicitly_global
5508        || components.len() < 2
5509        || components.iter().any(|component| component.is_template_id)
5510    {
5511        return None;
5512    }
5513    let names = components
5514        .iter()
5515        .map(|component| component.name.as_str())
5516        .collect::<Vec<_>>();
5517    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
5518    if class_chain.is_empty() {
5519        return None;
5520    }
5521    Some(class_chain.iter().map(|name| name.to_string()).collect())
5522}
5523
5524fn structured_cpp_qualified_components(
5525    qualified_name: Node<'_>,
5526    source: &str,
5527) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
5528    if qualified_name.kind() != "qualified_identifier" {
5529        return None;
5530    }
5531
5532    let mut components = Vec::new();
5533    let mut current = qualified_name;
5534    let mut explicitly_global = false;
5535    loop {
5536        if current.kind() == "qualified_identifier" {
5537            if let Some(component) = current.child_by_field_name("scope") {
5538                components.push(canonical_cpp_qualified_component(component, source)?);
5539            } else if components.is_empty() {
5540                explicitly_global = true;
5541            } else {
5542                return None;
5543            }
5544            current = current.child_by_field_name("name")?;
5545        } else {
5546            components.push(canonical_cpp_qualified_component(current, source)?);
5547            break;
5548        }
5549    }
5550    Some((components, explicitly_global))
5551}
5552
5553fn split_structured_templated_cpp_name(
5554    declarator_name: Node<'_>,
5555    source: &str,
5556    scope: &ScopeInfo,
5557) -> Option<(Option<CppMemberOwner>, String, String)> {
5558    let (mut components, explicitly_global) =
5559        structured_cpp_qualified_components(declarator_name, source)?;
5560
5561    let terminal = components.pop()?;
5562    let owner_start = components
5563        .iter()
5564        .position(|component| component.is_template_id)?;
5565    let explicit_package = components[..owner_start]
5566        .iter()
5567        .map(|component| component.name.as_str())
5568        .collect::<Vec<_>>()
5569        .join("::");
5570    let explicit_package_is_empty = explicit_package.is_empty();
5571    let package_name = match (
5572        explicitly_global,
5573        scope.package_name.is_empty(),
5574        explicit_package_is_empty,
5575    ) {
5576        (true, _, _) => explicit_package,
5577        (false, _, true) => scope.package_name.clone(),
5578        (false, true, false) => explicit_package,
5579        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
5580    };
5581    // Same identity-split fallback as `split_cpp_name` (#1093): a template
5582    // specialization's owner class named with no namespace segment of its own
5583    // (`explicit_package` empty) at file scope (`explicitly_global` false)
5584    // with nothing enclosing (`package_name` still empty) has no structural
5585    // signal for its namespace besides an in-scope `using namespace X;`.
5586    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
5587    {
5588        cpp_using_directive_namespace_for_bare_owner(scope)
5589    } else {
5590        package_name
5591    };
5592    let owner_chain = components[owner_start..]
5593        .iter()
5594        .map(|component| component.name.clone())
5595        .collect::<Vec<_>>();
5596    if owner_chain.is_empty() || terminal.name.is_empty() {
5597        return None;
5598    }
5599
5600    Some((
5601        Some(CppMemberOwner::Chain(owner_chain)),
5602        terminal.name,
5603        package_name,
5604    ))
5605}
5606
5607fn canonical_cpp_qualified_component(
5608    mut component: Node<'_>,
5609    source: &str,
5610) -> Option<CppQualifiedNameComponent> {
5611    let mut is_template_id = false;
5612    loop {
5613        match component.kind() {
5614            "template_type" => {
5615                is_template_id = true;
5616                component = component.child_by_field_name("name")?;
5617            }
5618            "dependent_name" => component = component.named_child(0)?,
5619            "identifier"
5620            | "field_identifier"
5621            | "namespace_identifier"
5622            | "type_identifier"
5623            | "operator_name"
5624            | "destructor_name" => {
5625                let name = normalize_cpp_whitespace(node_text(component, source));
5626                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
5627                    name,
5628                    is_template_id,
5629                });
5630            }
5631            _ => component = component.child_by_field_name("name")?,
5632        }
5633    }
5634}
5635
5636fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
5637    match node.kind() {
5638        "identifier"
5639        | "field_identifier"
5640        | "type_identifier"
5641        | "operator_name"
5642        | "destructor_name"
5643        | "qualified_identifier" => node_text(node, source).to_string(),
5644        "function_declarator"
5645        | "pointer_declarator"
5646        | "reference_declarator"
5647        | "parenthesized_declarator"
5648        | "array_declarator"
5649        | "template_function" => node
5650            .child_by_field_name("declarator")
5651            .or_else(|| node.child_by_field_name("name"))
5652            .or_else(|| last_named_child(node))
5653            .map(|child| extract_declarator_name(child, source))
5654            .unwrap_or_else(|| node_text(node, source).to_string()),
5655        _ => node
5656            .child_by_field_name("name")
5657            .map(|child| extract_declarator_name(child, source))
5658            .unwrap_or_else(|| node_text(node, source).to_string()),
5659    }
5660}
5661
5662/// Extract a callable identity only through declaration-shaped AST nodes.
5663/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
5664/// expose the call's parameter list as a false function declarator; accepting
5665/// arbitrary node text there emitted bogus names such as `.*f`.
5666fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5667    match node.kind() {
5668        "identifier"
5669        | "field_identifier"
5670        | "type_identifier"
5671        | "operator_name"
5672        | "destructor_name"
5673        | "qualified_identifier" => Some(node_text(node, source).to_string()),
5674        "function_declarator"
5675        | "pointer_declarator"
5676        | "reference_declarator"
5677        | "parenthesized_declarator"
5678        | "array_declarator"
5679        | "template_function" => node
5680            .child_by_field_name("declarator")
5681            .or_else(|| node.child_by_field_name("name"))
5682            .and_then(|child| extract_callable_declarator_name(child, source)),
5683        _ => None,
5684    }
5685}
5686
5687fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
5688    match node.kind() {
5689        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
5690            let name = node_text(node, source).trim().to_string();
5691            (!name.is_empty()).then_some(name)
5692        }
5693        _ => node
5694            .child_by_field_name("declarator")
5695            .or_else(|| node.child_by_field_name("name"))
5696            .or_else(|| last_named_child(node))
5697            .and_then(|child| extract_variable_name(child, source)),
5698    }
5699}
5700
5701fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
5702    let count = node.named_child_count();
5703    if count == 0 {
5704        None
5705    } else {
5706        node.named_child(count - 1)
5707    }
5708}
5709
5710fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
5711    let name_node = node.child_by_field_name("name")?;
5712    let name = normalize_cpp_whitespace(node_text(name_node, source));
5713    (!name.is_empty()).then_some(name)
5714}
5715
5716fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5717    if node.kind() != "declaration" {
5718        return Vec::new();
5719    }
5720    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
5721        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
5722    }) else {
5723        return Vec::new();
5724    };
5725    let Some(declarator) = node.child_by_field_name("declarator") else {
5726        return Vec::new();
5727    };
5728    if node_text(keyword, source) == "using"
5729        && (declarator.kind() != "init_declarator"
5730            || declarator.child_by_field_name("value").is_none())
5731    {
5732        return Vec::new();
5733    }
5734    if node_text(keyword, source) == "typedef"
5735        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
5736    {
5737        return vec![alias_name];
5738    }
5739    extract_typedef_declarator_name(declarator, source)
5740        .into_iter()
5741        .collect()
5742}
5743
5744fn recovered_typedef_error_alias_name(
5745    declaration: Node<'_>,
5746    declarator: Node<'_>,
5747    source: &str,
5748) -> Option<String> {
5749    // An export macro between `class` and its name can make tree-sitter parse
5750    // the recovered class body as a function body. In that shape,
5751    //
5752    //     typedef spi::Filter BASE_CLASS;
5753    //
5754    // becomes a declaration whose `declarator` is the underlying qualified
5755    // type (`spi::Filter`) and whose actual alias name is displaced into the
5756    // following ERROR node. Do not publish the terminal underlying type
5757    // (`Filter`) as a false class-owned alias.
5758    if declarator.kind() != "qualified_identifier" {
5759        return None;
5760    }
5761    let mut cursor = declaration.walk();
5762    let mut errors = declaration
5763        .named_children(&mut cursor)
5764        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
5765    let error = errors.next()?;
5766    if errors.next().is_some() || error.named_child_count() != 1 {
5767        return None;
5768    }
5769    let name = error.named_child(0)?;
5770    if !matches!(
5771        name.kind(),
5772        "identifier" | "field_identifier" | "type_identifier"
5773    ) {
5774        return None;
5775    }
5776    let name = normalize_cpp_whitespace(node_text(name, source));
5777    (!name.is_empty()).then_some(name)
5778}
5779
5780fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
5781    // A function-like token in the type position can make tree-sitter expose
5782    // its argument as a parenthesized declarator. Do not publish that argument
5783    // as an alias. The macro-specific recovery below handles the proven shape.
5784    if fragmented_parenthesized_typedef_type(node).is_some() {
5785        return Vec::new();
5786    }
5787    let has_function_like_macro_type = node
5788        .child_by_field_name("type")
5789        .filter(|type_node| type_node.kind() == "type_identifier")
5790        .is_some_and(|type_node| {
5791            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5792        });
5793    let mut names = Vec::new();
5794    let mut cursor = node.walk();
5795    for declarator in node.children_by_field_name("declarator", &mut cursor) {
5796        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
5797            continue;
5798        }
5799        if let Some(name) = extract_typedef_declarator_name(declarator, source)
5800            && !names.contains(&name)
5801        {
5802            names.push(name);
5803        }
5804    }
5805    names
5806}
5807
5808struct RecoveredMacroTypedefAlias<'tree> {
5809    name: String,
5810    end_node: Node<'tree>,
5811}
5812
5813/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
5814/// into an identifier expression statement. The uppercase macro token, missing
5815/// typedef terminator, and complete sibling terminator prove this exact shape.
5816fn recovered_macro_typedef_alias<'tree>(
5817    node: Node<'tree>,
5818    source: &str,
5819) -> Option<RecoveredMacroTypedefAlias<'tree>> {
5820    let type_node = fragmented_parenthesized_typedef_type(node)?;
5821    if type_node.kind() != "type_identifier"
5822        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
5823    {
5824        return None;
5825    }
5826
5827    let end_node = node.next_named_sibling()?;
5828    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
5829        return None;
5830    }
5831    let name_node = end_node.named_child(0)?;
5832    if name_node.kind() != "identifier" {
5833        return None;
5834    }
5835    let has_terminator = (0..end_node.child_count()).any(|index| {
5836        end_node
5837            .child(index)
5838            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
5839    });
5840    if !has_terminator {
5841        return None;
5842    }
5843    let name = normalize_cpp_whitespace(node_text(name_node, source));
5844    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
5845}
5846
5847fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
5848    if node.kind() != "type_definition" {
5849        return None;
5850    }
5851    let mut declarator_cursor = node.walk();
5852    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
5853    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
5854        return None;
5855    }
5856    let has_missing_terminator = (0..node.child_count()).any(|index| {
5857        node.child(index)
5858            .is_some_and(|child| child.kind() == ";" && child.is_missing())
5859    });
5860    if !has_missing_terminator {
5861        return None;
5862    }
5863    node.child_by_field_name("type")
5864}
5865
5866fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
5867    match node.kind() {
5868        "identifier" | "field_identifier" | "type_identifier" => {
5869            let name = normalize_cpp_whitespace(node_text(node, source));
5870            (!name.is_empty()).then_some(name)
5871        }
5872        "qualified_identifier" => node
5873            .child_by_field_name("name")
5874            .and_then(|name| extract_typedef_declarator_name(name, source)),
5875        _ => node
5876            .child_by_field_name("declarator")
5877            .or_else(|| node.child_by_field_name("name"))
5878            .or_else(|| last_named_child(node))
5879            .and_then(|child| extract_typedef_declarator_name(child, source)),
5880    }
5881}
5882
5883fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
5884    let name = node
5885        .child_by_field_name("name")
5886        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5887        .or_else(|| {
5888            let mut cursor = node.walk();
5889            node.named_children(&mut cursor)
5890                .find(|child| {
5891                    matches!(
5892                        child.kind(),
5893                        "identifier" | "field_identifier" | "type_identifier"
5894                    )
5895                })
5896                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
5897        })?;
5898    (!name.is_empty()).then_some(name)
5899}
5900
5901fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
5902    left.id() == right.id()
5903}
5904
5905fn render_cpp_type_signature(
5906    node: Node<'_>,
5907    source: &str,
5908    template_signature: Option<&str>,
5909) -> String {
5910    let text = normalize_cpp_whitespace(node_text(node, source));
5911    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
5912    let rendered = if head.ends_with(';') {
5913        head.to_string()
5914    } else {
5915        format!("{head} {{")
5916    };
5917    if let Some(template_signature) = template_signature {
5918        format!("template {template_signature} {rendered}")
5919    } else {
5920        rendered
5921    }
5922}
5923
5924fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
5925    if let Some(signature) =
5926        render_recovered_macro_qualified_field_signature(node, declarator, source)
5927    {
5928        return signature;
5929    }
5930    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
5931    let prefix = cpp_declaration_prefix(node, source);
5932    let name = extract_variable_name(declarator, source).unwrap_or_default();
5933    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
5934    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
5935        || (prefix.ends_with('&') && raw_suffix == "&")
5936    {
5937        String::new()
5938    } else {
5939        raw_suffix
5940    };
5941
5942    let mut rendered = if suffix.is_empty() {
5943        format!("{prefix} {name}")
5944    } else if suffix.starts_with('*') || suffix.starts_with('&') {
5945        format!("{prefix}{suffix} {name}")
5946    } else if suffix.starts_with('[') || suffix.starts_with('(') {
5947        format!("{prefix} {name}{suffix}")
5948    } else {
5949        format!("{prefix} {suffix}{name}")
5950    };
5951    rendered = collapse_cpp_whitespace(&rendered);
5952
5953    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
5954        format!("{rendered} = {initializer};")
5955    } else if declaration_text.ends_with(';') {
5956        format!("{rendered};")
5957    } else {
5958        rendered
5959    }
5960}
5961
5962fn render_recovered_macro_qualified_field_signature(
5963    node: Node<'_>,
5964    declarator: Node<'_>,
5965    source: &str,
5966) -> Option<String> {
5967    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
5968    if !recovered
5969        .iter()
5970        .any(|candidate| same_node(*candidate, declarator))
5971    {
5972        return None;
5973    }
5974    let pseudo_declarator = node.child_by_field_name("declarator")?;
5975    let mut cursor = node.walk();
5976    let clause = node
5977        .named_children(&mut cursor)
5978        .find(|child| child.kind() == "bitfield_clause")?;
5979    let mut cursor = clause.walk();
5980    let error = clause
5981        .named_children(&mut cursor)
5982        .find(|child| child.kind() == "ERROR")?;
5983    let qualified_type =
5984        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
5985    let prefix = cpp_declaration_prefix(node, source);
5986    let name = extract_variable_name(declarator, source)?;
5987    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
5988    let mut rendered = if suffix.is_empty() {
5989        format!("{prefix} {qualified_type} {name}")
5990    } else {
5991        format!("{prefix} {qualified_type} {suffix} {name}")
5992    };
5993    rendered = collapse_cpp_whitespace(&rendered);
5994
5995    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
5996        Some(format!(
5997            "{rendered} = {};",
5998            normalize_cpp_whitespace(node_text(initializer, source))
5999        ))
6000    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
6001        Some(format!("{rendered} = {initializer};"))
6002    } else {
6003        Some(format!("{rendered};"))
6004    }
6005}
6006
6007fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
6008    match node.kind() {
6009        "pointer_expression" => {
6010            let operator = node
6011                .child_by_field_name("operator")
6012                .or_else(|| node.child(0))
6013                .map(|operator| node_text(operator, source))
6014                .unwrap_or("*");
6015            let argument = node
6016                .child_by_field_name("argument")
6017                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6018                .unwrap_or_default();
6019            format!("{operator}{argument}")
6020        }
6021        "unary_expression" => {
6022            let operator = node
6023                .child_by_field_name("operator")
6024                .or_else(|| node.child(0))
6025                .map(|operator| node_text(operator, source))
6026                .unwrap_or_default();
6027            let argument = node
6028                .child_by_field_name("argument")
6029                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
6030                .unwrap_or_default();
6031            format!("{operator}{argument}")
6032        }
6033        "identifier" | "field_identifier" => String::new(),
6034        _ => cpp_declarator_suffix_without_name(node, source),
6035    }
6036}
6037
6038fn recovered_macro_qualified_field_initializer<'tree>(
6039    clause: Node<'tree>,
6040    declarator: Node<'tree>,
6041) -> Option<Node<'tree>> {
6042    let mut stack = vec![clause];
6043    while let Some(current) = stack.pop() {
6044        if current.kind() == "assignment_expression"
6045            && current
6046                .child_by_field_name("left")
6047                .is_some_and(|left| same_node(left, declarator))
6048        {
6049            return current.child_by_field_name("right");
6050        }
6051        let mut cursor = current.walk();
6052        stack.extend(current.named_children(&mut cursor));
6053    }
6054    None
6055}
6056
6057fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
6058    let text = node_text(node, source);
6059    let mut cursor = node.walk();
6060    let first_declarator = node.named_children(&mut cursor).find(|child| {
6061        matches!(
6062            child.kind(),
6063            "init_declarator"
6064                | "identifier"
6065                | "field_identifier"
6066                | "pointer_declarator"
6067                | "reference_declarator"
6068                | "array_declarator"
6069                | "function_declarator"
6070        )
6071    });
6072    let prefix = if let Some(first_declarator) = first_declarator {
6073        let end = first_declarator
6074            .start_byte()
6075            .saturating_sub(node.start_byte());
6076        let mut prefix = text.get(..end).unwrap_or(text).to_string();
6077        let declarator_suffix = match first_declarator.kind() {
6078            "init_declarator" => first_declarator
6079                .child_by_field_name("declarator")
6080                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
6081                .unwrap_or_default(),
6082            _ => cpp_declarator_suffix_without_name(first_declarator, source),
6083        };
6084        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
6085            prefix.push_str(&declarator_suffix);
6086        }
6087        return collapse_cpp_whitespace(&prefix)
6088            .trim_end_matches(',')
6089            .trim_end_matches(';')
6090            .trim()
6091            .to_string();
6092    } else {
6093        text
6094    };
6095    collapse_cpp_whitespace(prefix)
6096        .trim_end_matches(',')
6097        .trim_end_matches(';')
6098        .trim()
6099        .to_string()
6100}
6101
6102fn cpp_preserved_initializer(
6103    declaration_node: Node<'_>,
6104    declarator: Node<'_>,
6105    source: &str,
6106) -> Option<String> {
6107    let name = extract_variable_name(declarator, source)?;
6108    let mut cursor = declaration_node.walk();
6109    for child in declaration_node.named_children(&mut cursor) {
6110        if child.kind() != "init_declarator" {
6111            continue;
6112        }
6113        let Some(inner) = child.child_by_field_name("declarator") else {
6114            continue;
6115        };
6116        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
6117            continue;
6118        }
6119        let value = child.child_by_field_name("value")?;
6120        let kind = value.kind();
6121        if matches!(
6122            kind,
6123            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
6124        ) {
6125            return Some(normalize_cpp_whitespace(node_text(value, source)));
6126        }
6127        break;
6128    }
6129    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
6130    let pattern = format!(
6131        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
6132        regex::escape(&name)
6133    );
6134    Regex::new(&pattern)
6135        .ok()
6136        .and_then(|regex| regex.captures(&declaration_text))
6137        .and_then(|captures| captures.get(1))
6138        .map(|value| value.as_str().to_string())
6139}
6140
6141fn render_cpp_function_display_signature_from_node<'tree>(
6142    node: Node<'tree>,
6143    source: &str,
6144    template_signature: Option<&str>,
6145    has_body: bool,
6146    ancestry: &ParentIndex<'tree>,
6147) -> String {
6148    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
6149    let parent_text = node_text(root, source);
6150    let body_local_start = root
6151        .child_by_field_name("body")
6152        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
6153        .unwrap_or(parent_text.len());
6154    let display = parent_text
6155        .get(..body_local_start)
6156        .unwrap_or(parent_text)
6157        .trim()
6158        .trim();
6159    let display = if let Some(template_signature) = template_signature {
6160        if display.starts_with("template ") {
6161            display.to_string()
6162        } else {
6163            format!("template {template_signature} {display}")
6164        }
6165    } else {
6166        display.to_string()
6167    };
6168    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
6169    if has_body {
6170        format!("{display} {{...}}")
6171    } else {
6172        format!("{display};")
6173    }
6174}
6175
6176fn cpp_template_signature(
6177    template_node: Node<'_>,
6178    declaration_child: Node<'_>,
6179    source: &str,
6180) -> Option<String> {
6181    let text = source
6182        .get(template_node.start_byte()..declaration_child.start_byte())
6183        .unwrap_or("");
6184    let text = normalize_cpp_whitespace(text);
6185    let start = text.find('<')?;
6186    let end = text.rfind('>')?;
6187    if end < start {
6188        return None;
6189    }
6190    Some(text[start..=end].to_string())
6191}
6192
6193struct RecoveredFragmentedPartialSpecialization<'tree> {
6194    declaration_node: Node<'tree>,
6195    name: String,
6196    range: Range,
6197    prefix_members: Vec<Node<'tree>>,
6198    member_siblings: Vec<Node<'tree>>,
6199    following_declarations: Vec<Node<'tree>>,
6200}
6201
6202struct RecoveredFragmentedPreprocessorClass<'tree> {
6203    declaration_node: Node<'tree>,
6204    class_node: Node<'tree>,
6205    body: Node<'tree>,
6206    name: String,
6207    range: Range,
6208    tail_members: Vec<Node<'tree>>,
6209    member_siblings: Vec<Node<'tree>>,
6210}
6211
6212/// Recover a class whose preprocessor-fragmented parse closes at an early
6213/// member body and publishes the remaining in-class declarations as siblings
6214/// of the surrounding alternative. Primary classes are admitted only when an
6215/// earlier branch contains the matching bodyless declaration and the class
6216/// node retains the displaced `#endif`. Partial specializations instead carry
6217/// their identity structurally in the `template_type` name and template
6218/// metadata. Retain the original AST nodes and re-own only the siblings through
6219/// the displaced structural `};` terminator.
6220fn recover_fragmented_preprocessor_class<'tree>(
6221    template_node: Node<'tree>,
6222    source: &str,
6223    ancestry: &ParentIndex<'tree>,
6224) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
6225    let alternative = ancestry.parent(template_node)?;
6226    if alternative.kind() != "preproc_else" {
6227        return None;
6228    }
6229    let conditional = alternative.parent()?;
6230    if conditional.kind() != "preproc_if" {
6231        return None;
6232    }
6233    let declaration_node = template_node
6234        .named_children(&mut template_node.walk())
6235        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
6236    let class_node = declaration_node
6237        .named_children(&mut declaration_node.walk())
6238        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
6239    let body = cpp_body_node(class_node)?;
6240    if class_node.end_byte() >= declaration_node.end_byte() {
6241        return None;
6242    }
6243    let name = class_like_name(class_node, source, ancestry)?;
6244    let is_partial_specialization = class_node
6245        .child_by_field_name("name")
6246        .is_some_and(|class_name| class_name.kind() == "template_type");
6247    if is_partial_specialization {
6248        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
6249        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
6250            return None;
6251        }
6252    } else {
6253        if !class_has_displaced_preprocessor_terminator(class_node) {
6254            return None;
6255        }
6256        let matching_other_branch = conditional
6257            .named_children(&mut conditional.walk())
6258            .take_while(|child| !same_node(*child, alternative))
6259            .filter(|child| child.kind() == "template_declaration")
6260            .filter_map(first_class_like_child)
6261            .any(|candidate| {
6262                cpp_body_node(candidate).is_none()
6263                    && class_like_name(candidate, source, ancestry).as_deref()
6264                        == Some(name.as_str())
6265            });
6266        if !matching_other_branch {
6267            return None;
6268        }
6269    }
6270
6271    let mut tail_members = Vec::new();
6272    let mut saw_class = false;
6273    let mut declaration_cursor = declaration_node.walk();
6274    for child in declaration_node.named_children(&mut declaration_cursor) {
6275        if same_node(child, class_node) {
6276            saw_class = true;
6277        } else if saw_class {
6278            tail_members.push(child);
6279        }
6280    }
6281
6282    let mut member_siblings = Vec::new();
6283    let mut saw_template = false;
6284    let mut terminator = None;
6285    for index in 0..alternative.child_count() {
6286        let Some(child) = alternative.child(index) else {
6287            continue;
6288        };
6289        if same_node(child, template_node) {
6290            saw_template = true;
6291            continue;
6292        }
6293        if !saw_template {
6294            continue;
6295        }
6296        if displaced_fragmented_class_terminator(alternative, index) {
6297            terminator = alternative.child(index + 1);
6298            break;
6299        }
6300        if child.is_named() {
6301            member_siblings.push(child);
6302        }
6303    }
6304    let terminator = terminator?;
6305    Some(RecoveredFragmentedPreprocessorClass {
6306        declaration_node,
6307        class_node,
6308        body,
6309        name,
6310        range: Range {
6311            start_byte: class_node.start_byte(),
6312            end_byte: terminator.end_byte(),
6313            start_line: class_node.start_position().row + 1,
6314            end_line: terminator.end_position().row + 1,
6315        },
6316        tail_members,
6317        member_siblings,
6318    })
6319}
6320
6321fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
6322    (0..class_node.child_count()).any(|index| {
6323        class_node.child(index).is_some_and(|child| {
6324            child.kind() == "ERROR"
6325                && (0..child.child_count()).any(|error_index| {
6326                    child
6327                        .child(error_index)
6328                        .is_some_and(|token| token.kind() == "#endif")
6329                })
6330        })
6331    })
6332}
6333
6334/// The real `#endif` that tree-sitter consumed inside an error subtree.
6335///
6336/// A preprocessor directive inside a malformed array bound can cause later
6337/// declarations to remain children of the conditional. The non-missing token
6338/// still gives the exact structured boundary. Ignore nested conditionals and
6339/// select the last error-owned token. Tree-sitter can pair a later outer
6340/// `#endif` with this conditional, so the direct terminator is not necessarily
6341/// missing.
6342pub fn cpp_displaced_preprocessor_terminator<'tree>(
6343    conditional: Node<'tree>,
6344) -> Option<Node<'tree>> {
6345    if !conditional.has_error() {
6346        return None;
6347    }
6348    let has_concrete_direct_terminator = conditional
6349        .child_count()
6350        .checked_sub(1)
6351        .and_then(|index| conditional.child(index))
6352        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
6353    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
6354        // A structured alternative proves that the direct `#endif` closes
6355        // this family. An error-owned terminator inside either branch belongs
6356        // to a damaged nested conditional, not to this one.
6357        return None;
6358    }
6359    let mut displaced = None;
6360    let mut stack = (0..conditional.child_count())
6361        .filter_map(|index| conditional.child(index))
6362        .map(|child| (child, false))
6363        .collect::<Vec<_>>();
6364    while let Some((node, inside_error)) = stack.pop() {
6365        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
6366            continue;
6367        }
6368        if node.kind() == "#endif" && !node.is_missing() && inside_error {
6369            if displaced.is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte()) {
6370                displaced = Some(node);
6371            }
6372            continue;
6373        }
6374        if node != conditional
6375            && matches!(
6376                node.kind(),
6377                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
6378            )
6379        {
6380            continue;
6381        }
6382        let inside_error = inside_error || node.kind() == "ERROR";
6383        for index in 0..node.child_count() {
6384            if let Some(child) = node.child(index) {
6385                stack.push((child, inside_error));
6386            }
6387        }
6388    }
6389    displaced
6390}
6391
6392/// The effective end of a conditional whose real terminator tree-sitter
6393/// displaced into declaration recovery.
6394///
6395/// Most damaged conditionals retain a concrete `#endif` token below an
6396/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
6397/// boundary. A preprocessor family that selects the middle of a declaration
6398/// can lose the directive tokens entirely. In that shape tree-sitter leaves
6399/// the declaration's `typedef` token as the sole child of the immediately
6400/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
6401/// declarator name inside the conditional's first declaration. The declaration
6402/// end is then the smallest structured boundary that contains the whole split
6403/// declaration.
6404#[derive(Clone, Copy, Debug, Eq, PartialEq)]
6405pub struct CppDisplacedPreprocessorBoundary {
6406    pub end_byte: usize,
6407    pub end_line: usize,
6408}
6409
6410pub fn cpp_displaced_preprocessor_boundary(
6411    conditional: Node<'_>,
6412) -> Option<CppDisplacedPreprocessorBoundary> {
6413    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
6414        return Some(CppDisplacedPreprocessorBoundary {
6415            end_byte: terminator.end_byte(),
6416            end_line: terminator.end_position().row + 1,
6417        });
6418    }
6419    if let Some(declaration) = displaced_split_declaration(conditional) {
6420        return Some(CppDisplacedPreprocessorBoundary {
6421            end_byte: declaration.end_byte(),
6422            end_line: declaration.end_position().row + 1,
6423        });
6424    }
6425    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
6426        return Some(CppDisplacedPreprocessorBoundary {
6427            end_byte: terminator.end_byte(),
6428            end_line: terminator.end_position().row + 1,
6429        });
6430    }
6431    None
6432}
6433
6434fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6435    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
6436        return None;
6437    }
6438    let mut cursor = conditional.walk();
6439    let declarations = conditional
6440        .named_children(&mut cursor)
6441        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
6442        .collect::<Vec<_>>();
6443    let declaration = *declarations.first()?;
6444    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
6445        return None;
6446    }
6447    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
6448    let mut terminator = None;
6449    let mut stack = (0..declaration.child_count())
6450        .filter_map(|index| declaration.child(index))
6451        .filter(|child| child.start_byte() < declarator_start)
6452        .map(|child| (child, false))
6453        .collect::<Vec<_>>();
6454    while let Some((node, inside_error)) = stack.pop() {
6455        let inside_error = inside_error || node.kind() == "ERROR";
6456        if inside_error && node.kind() == "#endif" && !node.is_missing() {
6457            terminator = Some(node);
6458            continue;
6459        }
6460        for index in 0..node.child_count() {
6461            if let Some(child) = node.child(index)
6462                && child.start_byte() < declarator_start
6463            {
6464                stack.push((child, inside_error));
6465            }
6466        }
6467    }
6468    terminator
6469}
6470
6471fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
6472    if !conditional.has_error()
6473        || conditional.child_by_field_name("alternative").is_some()
6474        || conditional
6475            .prev_named_sibling()
6476            .filter(|sibling| {
6477                sibling.kind() == "ERROR"
6478                    && sibling.child_count() == 1
6479                    && sibling
6480                        .child(0)
6481                        .is_some_and(|child| child.kind() == "typedef")
6482            })
6483            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
6484            .is_none()
6485    {
6486        return None;
6487    }
6488    let mut cursor = conditional.walk();
6489    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
6490    let declaration_index = children
6491        .iter()
6492        .position(|child| child.kind() == "declaration" && child.has_error())?;
6493    let declaration = children[declaration_index];
6494    if !children
6495        .iter()
6496        .skip(declaration_index + 1)
6497        .any(|child| child.end_byte() > declaration.end_byte())
6498    {
6499        return None;
6500    }
6501    let declarator = declaration.child_by_field_name("declarator")?;
6502    let mut error_end = None;
6503    let mut names = Vec::new();
6504    let mut stack = vec![declarator];
6505    while let Some(node) = stack.pop() {
6506        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
6507            error_end =
6508                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
6509            continue;
6510        }
6511        if matches!(node.kind(), "identifier" | "type_identifier") {
6512            names.push(node.start_byte());
6513        }
6514        for index in (0..node.named_child_count()).rev() {
6515            if let Some(child) = node.named_child(index) {
6516                stack.push(child);
6517            }
6518        }
6519    }
6520    let error_end = error_end?;
6521    names
6522        .into_iter()
6523        .any(|start| start >= error_end)
6524        .then_some(declaration)
6525}
6526
6527fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
6528    let Some(error) = parent.child(error_index) else {
6529        return false;
6530    };
6531    if error.kind() != "ERROR"
6532        || error.child_count() != 1
6533        || error.child(0).is_none_or(|child| child.kind() != "}")
6534    {
6535        return false;
6536    }
6537    let Some(semicolon) = parent.child(error_index + 1) else {
6538        return false;
6539    };
6540    semicolon.kind() == "expression_statement"
6541        && semicolon.child_count() == 1
6542        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
6543}
6544
6545/// Locate the real end of a class-like declaration when a macro invocation
6546/// without a source semicolon absorbs the class's `};` into its parsed field.
6547/// The grammar then keeps following namespace declarations as later children
6548/// of the same field list. The direct ERROR-plus-semicolon pair proves the
6549/// boundary structurally; no source-text delimiter scan is needed.
6550fn displaced_macro_class_tail(
6551    declaration_node: Node<'_>,
6552    body: Node<'_>,
6553    source: &str,
6554) -> Option<DisplacedMacroClassTail> {
6555    if !matches!(
6556        declaration_node.kind(),
6557        "class_specifier" | "struct_specifier" | "union_specifier"
6558    ) || body.kind() != "field_declaration_list"
6559    {
6560        return None;
6561    }
6562
6563    let child_count = body.named_child_count();
6564    for index in 0..child_count {
6565        let child = body.named_child(index)?;
6566        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
6567            continue;
6568        };
6569        let split_index = index + 1;
6570        if split_index >= child_count {
6571            return None;
6572        }
6573        let mut cursor = body.walk();
6574        if !body
6575            .named_children(&mut cursor)
6576            .skip(split_index)
6577            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
6578        {
6579            return None;
6580        }
6581        return Some(DisplacedMacroClassTail {
6582            split_index,
6583            class_range: Range {
6584                start_byte: declaration_node.start_byte(),
6585                end_byte: terminator.end_byte(),
6586                start_line: declaration_node.start_position().row + 1,
6587                end_line: terminator.end_position().row + 1,
6588            },
6589        });
6590    }
6591    None
6592}
6593
6594fn displaced_macro_field_terminator<'tree>(
6595    field: Node<'tree>,
6596    source: &str,
6597) -> Option<Node<'tree>> {
6598    if field.kind() != "field_declaration" {
6599        return None;
6600    }
6601    let macro_type = field.child_by_field_name("type")?;
6602    if macro_type.kind() != "type_identifier"
6603        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
6604        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
6605    {
6606        return None;
6607    }
6608    for index in 0..field.child_count() {
6609        let error = field.child(index)?;
6610        if error.kind() != "ERROR"
6611            || error.child_count() != 1
6612            || error.child(0).is_none_or(|child| child.kind() != "}")
6613        {
6614            continue;
6615        }
6616        let semicolon = field.child(index + 1)?;
6617        if semicolon.kind() == ";" {
6618            return Some(semicolon);
6619        }
6620    }
6621    None
6622}
6623
6624fn recover_fragmented_partial_specialization<'tree>(
6625    template_node: Node<'tree>,
6626    declaration_child: Node<'tree>,
6627    source: &str,
6628    ancestry: &ParentIndex<'tree>,
6629) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
6630    if declaration_child.kind() != "function_definition" {
6631        return None;
6632    }
6633    let class_node = declaration_child.child_by_field_name("type")?;
6634    if !matches!(
6635        class_node.kind(),
6636        "class_specifier" | "struct_specifier" | "union_specifier"
6637    ) || !class_node
6638        .child_by_field_name("name")
6639        .and_then(|name| direct_identifier_name(name, source))
6640        .is_some_and(|name| cpp_export_macro_token(&name))
6641    {
6642        return None;
6643    }
6644    let declarator = declaration_child.child_by_field_name("declarator")?;
6645    if declarator.kind() != "template_function" {
6646        return None;
6647    }
6648    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
6649    if metadata.specialization_arguments.is_empty() {
6650        return None;
6651    }
6652    let body = declaration_child.child_by_field_name("body")?;
6653    if body.kind() != "compound_statement" {
6654        return None;
6655    }
6656    let complete_prefix = body.named_child(0).filter(|first| {
6657        first.kind() == "labeled_statement"
6658            && first.has_error()
6659            && first
6660                .named_child(first.named_child_count().saturating_sub(1))
6661                .is_some_and(recovered_declaration_has_class_terminator)
6662    });
6663    let complete_body = complete_prefix.is_some();
6664    let mut prefix_members = Vec::new();
6665    if let Some(prefix) = complete_prefix {
6666        prefix_members.push(prefix);
6667    } else {
6668        let mut body_cursor = body.walk();
6669        for child in body.named_children(&mut body_cursor) {
6670            if !is_structurally_valid_fragmented_class_prefix_member(child) {
6671                break;
6672            }
6673            prefix_members.push(child);
6674        }
6675    }
6676    let containing_declarations = template_node.parent()?;
6677    if !matches!(
6678        containing_declarations.kind(),
6679        "declaration_list" | "compound_statement"
6680    ) {
6681        return None;
6682    }
6683    let mut member_siblings = Vec::new();
6684    let mut following_declarations = Vec::new();
6685    let terminator;
6686    if complete_body {
6687        terminator = complete_prefix?;
6688        let mut cursor = body.walk();
6689        let mut after_prefix = false;
6690        for child in body.named_children(&mut cursor) {
6691            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
6692                after_prefix = true;
6693            } else if after_prefix {
6694                following_declarations.push(child);
6695            }
6696        }
6697    } else {
6698        let mut found_template = false;
6699        let mut cursor = containing_declarations.walk();
6700        let mut class_terminator = None;
6701        for child in containing_declarations.children(&mut cursor) {
6702            if same_node(child, template_node) {
6703                found_template = true;
6704                continue;
6705            }
6706            if found_template && child.kind() == "}" {
6707                class_terminator = Some(child);
6708                break;
6709            }
6710            // A namespace can never be a class member: reaching one before the
6711            // terminator proves the class's true close was swallowed upstream
6712            // and this scan has crossed into the enclosing scope, so the
6713            // recovery cannot be bounded -- continuing re-owns the namespace
6714            // block (and its template specializations) as class members under
6715            // a re-appended package, desyncing the fq boundary (#2306).
6716            if found_template && child.kind() == "namespace_definition" {
6717                return None;
6718            }
6719            if found_template && child.is_named() {
6720                member_siblings.push(child);
6721            }
6722        }
6723        terminator = class_terminator?;
6724    }
6725    let name = format!(
6726        "{}<{}>",
6727        metadata.primary_name,
6728        metadata
6729            .specialization_arguments
6730            .iter()
6731            .map(|argument| argument.text.as_str())
6732            .collect::<Vec<_>>()
6733            .join(", ")
6734    );
6735    Some(RecoveredFragmentedPartialSpecialization {
6736        declaration_node: declaration_child,
6737        name,
6738        range: Range {
6739            start_byte: declaration_child.start_byte(),
6740            end_byte: terminator.end_byte(),
6741            start_line: declaration_child.start_position().row + 1,
6742            end_line: terminator.end_position().row + 1,
6743        },
6744        prefix_members,
6745        member_siblings,
6746        following_declarations,
6747    })
6748}
6749
6750fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
6751    if declaration.kind() != "declaration" {
6752        return false;
6753    }
6754    // With an export macro between `class` and its name, tree-sitter folds a
6755    // complete class body into a function-shaped declaration. The class's own
6756    // `};` remains structurally identifiable as a direct ERROR child holding
6757    // `}`, immediately followed by the declaration's direct `;` child.
6758    (0..declaration.child_count().saturating_sub(1)).any(|index| {
6759        let Some(error) = declaration.child(index) else {
6760            return false;
6761        };
6762        error.kind() == "ERROR"
6763            && error.child_count() == 1
6764            && error.child(0).is_some_and(|child| child.kind() == "}")
6765            && declaration
6766                .child(index + 1)
6767                .is_some_and(|child| child.kind() == ";")
6768    })
6769}
6770
6771fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
6772    if node.has_error() {
6773        return false;
6774    }
6775    match node.kind() {
6776        "declaration"
6777        | "field_declaration"
6778        | "alias_declaration"
6779        | "type_definition"
6780        | "static_assert_declaration" => true,
6781        "labeled_statement" => node
6782            .named_child(node.named_child_count().saturating_sub(1))
6783            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
6784        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
6785            matches!(
6786                child.kind(),
6787                "declaration"
6788                    | "field_declaration"
6789                    | "alias_declaration"
6790                    | "type_definition"
6791                    | "function_definition"
6792            )
6793        }),
6794        _ => false,
6795    }
6796}
6797
6798fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
6799    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
6800        .then(|| node.child_by_field_name("declarator"))
6801        .flatten()
6802        .and_then(|declarator| extract_variable_name(declarator, source))
6803}
6804
6805fn cpp_template_metadata<'tree>(
6806    template_node: Node<'tree>,
6807    declaration_child: Node<'tree>,
6808    source: &str,
6809    ancestry: &ParentIndex<'tree>,
6810) -> Option<CppTemplateMetadata> {
6811    let parameters_node = template_node.child_by_field_name("parameters")?;
6812    let name_node = cpp_templated_class_name_node(declaration_child)?;
6813    let primary_node = match name_node.kind() {
6814        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
6815        _ => name_node,
6816    };
6817    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
6818    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
6819        return None;
6820    }
6821
6822    let mut parameter_nodes = Vec::new();
6823    let mut parameter_names = Vec::new();
6824    let mut cursor = parameters_node.walk();
6825    for parameter in parameters_node.named_children(&mut cursor) {
6826        if !matches!(
6827            parameter.kind(),
6828            "type_parameter_declaration"
6829                | "optional_type_parameter_declaration"
6830                | "variadic_type_parameter_declaration"
6831                | "template_template_parameter_declaration"
6832                | "parameter_declaration"
6833                | "optional_parameter_declaration"
6834                | "variadic_parameter_declaration"
6835        ) {
6836            continue;
6837        }
6838        let index = parameter_nodes.len();
6839        // An unnamed parameter still contributes template arity and kind. Use
6840        // an impossible C++ identifier so positional reconciliation can bind
6841        // it without making source expressions refer to a name that was not
6842        // written.
6843        let name = cpp_template_parameter_name(parameter, source)
6844            .unwrap_or_else(|| format!("<anonymous:{index}>"));
6845        parameter_names.push(name);
6846        parameter_nodes.push(parameter);
6847    }
6848    let parameters = parameter_nodes
6849        .into_iter()
6850        .zip(parameter_names.iter().cloned())
6851        .map(|(parameter, name)| CppTemplateParameterMetadata {
6852            name,
6853            kind: cpp_template_parameter_kind(parameter),
6854            variadic: matches!(
6855                parameter.kind(),
6856                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
6857            ),
6858            default: cpp_template_parameter_default_expression(
6859                parameter,
6860                source,
6861                &parameter_names,
6862                ancestry,
6863            ),
6864        })
6865        .collect();
6866    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
6867        Vec::new()
6868    } else {
6869        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
6870            .unwrap_or_default()
6871    };
6872    let alias_target = (declaration_child.kind() == "alias_declaration")
6873        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
6874        .flatten();
6875    Some(CppTemplateMetadata {
6876        primary_name,
6877        primary_fq_name: String::new(),
6878        parameters,
6879        specialization_arguments,
6880        alias_target,
6881    })
6882}
6883
6884fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
6885    match node.kind() {
6886        "class_specifier" | "struct_specifier" | "union_specifier" => {
6887            node.child_by_field_name("name")
6888        }
6889        "function_definition" => {
6890            let declarator = node.child_by_field_name("declarator")?;
6891            if matches!(declarator.kind(), "identifier" | "template_function") {
6892                Some(declarator)
6893            } else {
6894                None
6895            }
6896        }
6897        "alias_declaration" => node.child_by_field_name("name"),
6898        _ => None,
6899    }
6900}
6901
6902fn cpp_template_alias_target<'tree>(
6903    alias: Node<'tree>,
6904    source: &str,
6905    parameter_names: &[String],
6906    ancestry: &ParentIndex<'tree>,
6907) -> Option<CppTemplateAliasTargetMetadata> {
6908    let mut type_node = alias.child_by_field_name("type")?;
6909    while type_node.kind() == "type_descriptor" {
6910        type_node = type_node.child_by_field_name("type")?;
6911    }
6912    let global = type_node.child_by_field_name("scope").is_none()
6913        && type_node.child(0).is_some_and(|child| child.kind() == "::");
6914    let mut components = Vec::new();
6915    cpp_template_target_components(type_node, source, &mut components)?;
6916    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
6917    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
6918        components,
6919        global,
6920        arguments,
6921    })
6922}
6923
6924fn cpp_template_target_components(
6925    node: Node<'_>,
6926    source: &str,
6927    out: &mut Vec<String>,
6928) -> Option<()> {
6929    match node.kind() {
6930        "identifier" | "namespace_identifier" | "type_identifier" => {
6931            out.push(node_text(node, source).to_string());
6932            Some(())
6933        }
6934        "template_type" => {
6935            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6936        }
6937        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
6938            if let Some(scope) = node.child_by_field_name("scope") {
6939                cpp_template_target_components(scope, source, out)?;
6940            }
6941            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
6942        }
6943        _ => None,
6944    }
6945}
6946
6947fn cpp_template_argument_expressions<'tree>(
6948    mut node: Node<'tree>,
6949    source: &str,
6950    parameter_names: &[String],
6951    ancestry: &ParentIndex<'tree>,
6952) -> Option<Vec<CppTemplateExpression>> {
6953    loop {
6954        match node.kind() {
6955            "template_type" | "template_function" => {
6956                let arguments = node.child_by_field_name("arguments")?;
6957                let mut cursor = arguments.walk();
6958                return Some(
6959                    arguments
6960                        .named_children(&mut cursor)
6961                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
6962                        .map(|argument| {
6963                            cpp_template_expression(argument, source, parameter_names, ancestry)
6964                        })
6965                        .collect(),
6966                );
6967            }
6968            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
6969                node = node
6970                    .child_by_field_name("name")
6971                    .or_else(|| node.child_by_field_name("type"))?;
6972            }
6973            _ => return None,
6974        }
6975    }
6976}
6977
6978fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
6979    let candidate = node
6980        .child_by_field_name("name")
6981        .or_else(|| node.child_by_field_name("declarator"))
6982        .or_else(|| {
6983            let mut cursor = node.walk();
6984            node.named_children(&mut cursor).find(|child| {
6985                matches!(
6986                    child.kind(),
6987                    "identifier" | "type_identifier" | "field_identifier"
6988                )
6989            })
6990        })?;
6991    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
6992    (!name.is_empty()).then_some(name)
6993}
6994
6995fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
6996    match node.kind() {
6997        "type_parameter_declaration"
6998        | "optional_type_parameter_declaration"
6999        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
7000        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
7001        _ => CppTemplateParameterKind::Value,
7002    }
7003}
7004
7005fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
7006    node.child_by_field_name("default_type")
7007        .or_else(|| node.child_by_field_name("default_value"))
7008}
7009
7010fn cpp_template_parameter_default_expression<'tree>(
7011    parameter: Node<'tree>,
7012    source: &str,
7013    parameter_names: &[String],
7014    ancestry: &ParentIndex<'tree>,
7015) -> Option<CppTemplateExpression> {
7016    let default = cpp_template_parameter_default(parameter)?;
7017    let base = cpp_template_expression(default, source, parameter_names, ancestry);
7018    let Some(pointer_error) = parameter.next_named_sibling() else {
7019        return Some(base);
7020    };
7021    let Some(pointer_declarator) =
7022        recovered_abstract_pointer_declarator_term(pointer_error, source)
7023    else {
7024        return Some(base);
7025    };
7026    Some(CppTemplateExpression {
7027        text: format!(
7028            "{}{}",
7029            base.text,
7030            normalize_cpp_whitespace(node_text(pointer_error, source))
7031        ),
7032        term: CppTemplateTerm::Node {
7033            kind: "type_descriptor".to_string(),
7034            children: vec![base.term, pointer_declarator],
7035        },
7036    })
7037}
7038
7039fn recovered_abstract_pointer_declarator_term(
7040    node: Node<'_>,
7041    source: &str,
7042) -> Option<CppTemplateTerm> {
7043    if node.kind() != "ERROR" || node.child_count() == 0 {
7044        return None;
7045    }
7046    let mut children = Vec::new();
7047    for index in 0..node.child_count() {
7048        let child = node.child(index)?;
7049        if child.kind() != "*" {
7050            return None;
7051        }
7052        children.push(CppTemplateTerm::Atom {
7053            kind: "*".to_string(),
7054            text: normalize_cpp_whitespace(node_text(child, source)),
7055        });
7056    }
7057    Some(CppTemplateTerm::Node {
7058        kind: "abstract_pointer_declarator".to_string(),
7059        children,
7060    })
7061}
7062
7063fn cpp_template_expression<'tree>(
7064    node: Node<'tree>,
7065    source: &str,
7066    parameter_names: &[String],
7067    ancestry: &ParentIndex<'tree>,
7068) -> CppTemplateExpression {
7069    let text = normalize_cpp_whitespace(node_text(node, source));
7070    CppTemplateExpression {
7071        text,
7072        term: cpp_template_term(node, source, parameter_names, ancestry),
7073    }
7074}
7075
7076pub fn cpp_template_term<'tree>(
7077    node: Node<'tree>,
7078    source: &str,
7079    parameter_names: &[String],
7080    ancestry: &ParentIndex<'tree>,
7081) -> CppTemplateTerm {
7082    enum Work<'tree> {
7083        Visit(Node<'tree>),
7084        Build { kind: String, child_count: usize },
7085    }
7086
7087    let mut work = vec![Work::Visit(node)];
7088    let mut terms = Vec::new();
7089    while let Some(next) = work.pop() {
7090        match next {
7091            Work::Visit(current) => {
7092                let text = normalize_cpp_whitespace(node_text(current, source));
7093                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
7094                    terms.push(CppTemplateTerm::Parameter(text));
7095                    continue;
7096                }
7097                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
7098                    let mut cursor = current.walk();
7099                    let named = current
7100                        .named_children(&mut cursor)
7101                        .filter(|child| !child.is_extra() && child.kind() != "comment")
7102                        .collect::<Vec<_>>();
7103                    if let [child] = named.as_slice() {
7104                        work.push(Work::Visit(*child));
7105                        continue;
7106                    }
7107                }
7108                if current.child_count() == 0 {
7109                    terms.push(CppTemplateTerm::Atom {
7110                        kind: if matches!(
7111                            current.kind(),
7112                            "identifier"
7113                                | "type_identifier"
7114                                | "field_identifier"
7115                                | "namespace_identifier"
7116                        ) {
7117                            "identifier".to_string()
7118                        } else {
7119                            current.kind().to_string()
7120                        },
7121                        text,
7122                    });
7123                    continue;
7124                }
7125                let children = (0..current.child_count())
7126                    .filter_map(|index| current.child(index))
7127                    .filter(|child| !child.is_extra() && child.kind() != "comment")
7128                    .collect::<Vec<_>>();
7129                work.push(Work::Build {
7130                    kind: current.kind().to_string(),
7131                    child_count: children.len(),
7132                });
7133                work.extend(children.into_iter().rev().map(Work::Visit));
7134            }
7135            Work::Build { kind, child_count } => {
7136                let children = terms.split_off(terms.len() - child_count);
7137                terms.push(CppTemplateTerm::Node { kind, children });
7138            }
7139        }
7140    }
7141    terms.pop().expect("template term traversal emits one root")
7142}
7143
7144fn cpp_template_term_leaf_is_parameter<'tree>(
7145    node: Node<'tree>,
7146    text: &str,
7147    parameter_names: &[String],
7148    ancestry: &ParentIndex<'tree>,
7149) -> bool {
7150    if !parameter_names.iter().any(|parameter| parameter == text) {
7151        return false;
7152    }
7153    !ancestry.parent(node).is_some_and(|parent| {
7154        matches!(
7155            parent.kind(),
7156            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
7157        ) && parent.child_by_field_name("scope").is_some()
7158            && parent.child_by_field_name("name") == Some(node)
7159    })
7160}
7161
7162fn enclosing_cpp_declaration_node<'tree>(
7163    mut node: Node<'tree>,
7164    ancestry: &ParentIndex<'tree>,
7165) -> Option<Node<'tree>> {
7166    loop {
7167        match node.kind() {
7168            "declaration"
7169            | "function_declaration"
7170            | "field_declaration"
7171            | "function_definition" => return Some(node),
7172            _ => node = ancestry.parent(node)?,
7173        }
7174    }
7175}
7176
7177fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
7178    let mut params = Vec::new();
7179    let mut cursor = parameters_node.walk();
7180    for child in parameters_node.children(&mut cursor) {
7181        match child.kind() {
7182            "parameter_declaration" | "optional_parameter_declaration" => {
7183                params.push(cpp_parameter_type(child, source));
7184            }
7185            "variadic_parameter_declaration" => {
7186                params.push(cpp_parameter_type(child, source));
7187            }
7188            "variadic_parameter" | "..." => params.push("...".to_string()),
7189            _ => {}
7190        }
7191    }
7192
7193    if params.is_empty() {
7194        "()".to_string()
7195    } else {
7196        format!("({})", params.join(", "))
7197    }
7198}
7199
7200fn cpp_signature_metadata<'tree>(
7201    signature: String,
7202    function_declarator: Node<'tree>,
7203    source: &str,
7204    ancestry: &ParentIndex<'tree>,
7205) -> SignatureMetadata {
7206    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
7207    let enrich = |metadata: SignatureMetadata| metadata.with_dispatch_extensibility(dispatch);
7208    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
7209    let return_type_identity =
7210        cpp_callable_return_type_identity(function_declarator, source, ancestry);
7211    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7212        return enrich(
7213            SignatureMetadata::new(signature, Vec::new())
7214                .with_return_type_text(return_type_text)
7215                .with_return_type_identity(return_type_identity),
7216        );
7217    };
7218    let callable_arity = cpp_callable_arity(parameters_node, source);
7219    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
7220    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
7221    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
7222    let Some(relative_start) = signature
7223        .get(search_from..)
7224        .and_then(|suffix| suffix.find(&parameter_text))
7225    else {
7226        return enrich(
7227            SignatureMetadata::new(signature, Vec::new())
7228                .with_callable_arity(callable_arity)
7229                .with_callable_parameter_types(callable_parameter_types)
7230                .with_return_type_text(return_type_text)
7231                .with_return_type_identity(return_type_identity),
7232        );
7233    };
7234    let parameters_start = search_from + relative_start;
7235    let parameters_end = parameters_start + parameter_text.len();
7236    let mut search_start = parameters_start;
7237    let parameters = cpp_parameter_label_nodes(parameters_node)
7238        .into_iter()
7239        .filter_map(|label_node| {
7240            let label = normalize_cpp_whitespace(node_text(label_node, source));
7241            if label.is_empty() || search_start > parameters_end {
7242                return None;
7243            }
7244            let haystack = signature.get(search_start..parameters_end)?;
7245            let relative_start = haystack.find(&label)?;
7246            let start_byte = search_start + relative_start;
7247            let end_byte = start_byte + label.len();
7248            search_start = end_byte;
7249            Some(ParameterMetadata::new(label, start_byte, end_byte))
7250        })
7251        .collect();
7252    enrich(
7253        SignatureMetadata::new(signature, parameters)
7254            .with_callable_arity(callable_arity)
7255            .with_callable_parameter_types(callable_parameter_types)
7256            .with_return_type_text(return_type_text)
7257            .with_return_type_identity(return_type_identity),
7258    )
7259}
7260
7261fn cpp_callable_is_structural_constructor<'tree>(
7262    function_declarator: Node<'tree>,
7263    source: &str,
7264    ancestry: &ParentIndex<'tree>,
7265) -> bool {
7266    let Some(name_node) = function_declarator
7267        .child_by_field_name("declarator")
7268        .or_else(|| function_declarator.child_by_field_name("name"))
7269        .or_else(|| last_named_child(function_declarator))
7270    else {
7271        return false;
7272    };
7273    let Some(callable_name) = direct_identifier_name(name_node, source) else {
7274        return false;
7275    };
7276
7277    let mut current = ancestry.parent(function_declarator);
7278    while let Some(ancestor) = current {
7279        let owner_name = match ancestor.kind() {
7280            "class_specifier" | "struct_specifier" | "union_specifier" => {
7281                class_like_name(ancestor, source, ancestry)
7282            }
7283            "ERROR" => malformed_class_error_owner_name(ancestor, source),
7284            _ => None,
7285        };
7286        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
7287            return true;
7288        }
7289        current = ancestry.parent(ancestor);
7290    }
7291    false
7292}
7293
7294/// Recover the owner name from the direct grammar shape retained when a later
7295/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
7296/// `ERROR` node:
7297///
7298/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
7299///
7300/// Direct-child checks keep this distinct from an unrelated nested class inside
7301/// a broader error region. The closing brace may be displaced past the error
7302/// node, so the opening body token is the available structural boundary.
7303fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
7304    if node.kind() != "ERROR" {
7305        return None;
7306    }
7307    let keyword = node.child(0)?;
7308    if !matches!(keyword.kind(), "class" | "struct" | "union") {
7309        return None;
7310    }
7311    let name_node = node.child(1)?;
7312    let name = direct_identifier_name(name_node, source)?;
7313    let has_body = (2..node.child_count())
7314        .filter_map(|index| node.child(index))
7315        .any(|child| child.kind() == "{");
7316    has_body.then_some(name)
7317}
7318
7319fn cpp_callable_return_type_identity<'tree>(
7320    function_declarator: Node<'tree>,
7321    source: &str,
7322    ancestry: &ParentIndex<'tree>,
7323) -> Option<StructuredTypeIdentity> {
7324    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
7325        return None;
7326    }
7327    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
7328    if let Some((return_type, _)) =
7329        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
7330    {
7331        return cpp_structured_type_identity(return_type, source, &lexical_scope);
7332    }
7333    let mut cursor = function_declarator.walk();
7334    if let Some(trailing) = function_declarator
7335        .named_children(&mut cursor)
7336        .find(|child| child.kind() == "trailing_return_type")
7337        && let Some(type_descriptor) = trailing.named_child(0)
7338    {
7339        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
7340    }
7341
7342    let mut current = function_declarator;
7343    let mut wrappers = Vec::new();
7344    while let Some(parent) = ancestry.parent(current) {
7345        if matches!(
7346            parent.kind(),
7347            "function_definition" | "declaration" | "field_declaration"
7348        ) {
7349            let type_node = parent.child_by_field_name("type")?;
7350            if cpp_export_macro_token(node_text(type_node, source))
7351                && (0..parent.named_child_count()).any(|index| {
7352                    parent
7353                        .named_child(index)
7354                        .is_some_and(|child| child.kind() == "ERROR")
7355                })
7356            {
7357                return None;
7358            }
7359            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
7360            for wrapper in wrappers.into_iter().rev() {
7361                identity = cpp_wrap_structured_type(identity, wrapper)?;
7362            }
7363            return Some(identity);
7364        }
7365        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7366            || (matches!(
7367                parent.kind(),
7368                "pointer_declarator"
7369                    | "reference_declarator"
7370                    | "array_declarator"
7371                    | "parenthesized_declarator"
7372            ) && parent.named_child_count() == 1
7373                && parent.named_child(0) == Some(current));
7374        if !wraps_current_declarator {
7375            return None;
7376        }
7377        match parent.kind() {
7378            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
7379            "reference_declarator" => wrappers.push(CppStructuredTypeWrapper::Reference),
7380            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
7381            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
7382            _ => return None,
7383        }
7384        current = parent;
7385    }
7386    None
7387}
7388
7389fn cpp_structured_type_identity(
7390    node: Node<'_>,
7391    source: &str,
7392    lexical_scope: &[String],
7393) -> Option<StructuredTypeIdentity> {
7394    enum Work<'tree> {
7395        Visit(Node<'tree>),
7396        Wrap(CppStructuredTypeWrapper),
7397        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
7398        BuildGeneric { argument_count: usize },
7399    }
7400
7401    let mut work = vec![Work::Visit(node)];
7402    let mut values = Vec::new();
7403    let mut builder = StructuredTypeIdentityBuilder::default();
7404    while let Some(next) = work.pop() {
7405        match next {
7406            Work::Visit(current) => match current.kind() {
7407                "type_descriptor" => {
7408                    let type_node = current
7409                        .child_by_field_name("type")
7410                        .or_else(|| current.named_child(0))?;
7411                    let mut wrappers = Vec::new();
7412                    let mut cursor = current.walk();
7413                    for child in current.named_children(&mut cursor) {
7414                        if child.id() != type_node.id() {
7415                            wrappers.extend(cpp_structured_declarator_wrappers(child));
7416                        }
7417                    }
7418                    work.push(Work::ApplyWrappers(wrappers));
7419                    work.push(Work::Visit(type_node));
7420                }
7421                "pointer_declarator" | "abstract_pointer_declarator" => {
7422                    let child = current
7423                        .child_by_field_name("declarator")
7424                        .or_else(|| current.named_child(0))?;
7425                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
7426                    work.push(Work::Visit(child));
7427                }
7428                "reference_declarator" => {
7429                    let child = current
7430                        .child_by_field_name("declarator")
7431                        .or_else(|| current.named_child(0))?;
7432                    work.push(Work::Wrap(CppStructuredTypeWrapper::Reference));
7433                    work.push(Work::Visit(child));
7434                }
7435                "array_declarator" | "abstract_array_declarator" => {
7436                    let child = current
7437                        .child_by_field_name("declarator")
7438                        .or_else(|| current.named_child(0))?;
7439                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
7440                    work.push(Work::Visit(child));
7441                }
7442                "template_type" => {
7443                    let name_node = current.child_by_field_name("name")?;
7444                    let arguments = current
7445                        .child_by_field_name("arguments")
7446                        .map(|arguments_node| {
7447                            let mut cursor = arguments_node.walk();
7448                            arguments_node
7449                                .named_children(&mut cursor)
7450                                .filter(|child| !child.is_extra() && child.kind() != "comment")
7451                                .collect::<Vec<_>>()
7452                        })
7453                        .unwrap_or_default();
7454                    work.push(Work::BuildGeneric {
7455                        argument_count: arguments.len(),
7456                    });
7457                    work.extend(arguments.into_iter().rev().map(Work::Visit));
7458                    work.push(Work::Visit(name_node));
7459                }
7460                "qualified_identifier"
7461                | "scoped_identifier"
7462                | "scoped_type_identifier"
7463                | "type_identifier"
7464                | "field_identifier"
7465                | "identifier"
7466                | "namespace_identifier"
7467                | "primitive_type" => {
7468                    values.push(builder.named(cpp_structured_named_type(
7469                        current,
7470                        source,
7471                        lexical_scope,
7472                    )?)?);
7473                }
7474                _ => {
7475                    let child = current.child_by_field_name("type").or_else(|| {
7476                        (current.named_child_count() == 1)
7477                            .then(|| current.named_child(0))
7478                            .flatten()
7479                    })?;
7480                    work.push(Work::Visit(child));
7481                }
7482            },
7483            Work::Wrap(wrapper) => {
7484                let root = values.pop()?;
7485                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
7486            }
7487            Work::ApplyWrappers(wrappers) => {
7488                let mut root = values.pop()?;
7489                for wrapper in wrappers.into_iter().rev() {
7490                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
7491                }
7492                values.push(root);
7493            }
7494            Work::BuildGeneric { argument_count } => {
7495                let value_count = argument_count.checked_add(1)?;
7496                let start = values.len().checked_sub(value_count)?;
7497                let mut built = values.split_off(start);
7498                let base = built.remove(0);
7499                values.push(builder.generic(base, built)?);
7500            }
7501        }
7502    }
7503    (values.len() == 1)
7504        .then(|| values.pop())
7505        .flatten()
7506        .and_then(|root| builder.finish(root))
7507}
7508
7509fn cpp_structured_named_type(
7510    node: Node<'_>,
7511    source: &str,
7512    lexical_scope: &[String],
7513) -> Option<StructuredTypeName> {
7514    let path = cpp_structured_type_path(node, source)?;
7515    let absolute = node.child_by_field_name("scope").is_none()
7516        && node.child(0).is_some_and(|child| child.kind() == "::");
7517    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
7518}
7519
7520#[derive(Clone, Copy)]
7521enum CppStructuredTypeWrapper {
7522    Pointer,
7523    Reference,
7524    Array,
7525}
7526
7527fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Vec<CppStructuredTypeWrapper> {
7528    let mut wrappers = Vec::new();
7529    let mut current = node;
7530    loop {
7531        match current.kind() {
7532            "pointer_declarator" | "abstract_pointer_declarator" => {
7533                wrappers.push(CppStructuredTypeWrapper::Pointer)
7534            }
7535            "reference_declarator" | "abstract_reference_declarator" => {
7536                wrappers.push(CppStructuredTypeWrapper::Reference)
7537            }
7538            "array_declarator" | "abstract_array_declarator" => {
7539                wrappers.push(CppStructuredTypeWrapper::Array)
7540            }
7541            _ => break,
7542        }
7543        let Some(child) = current
7544            .child_by_field_name("declarator")
7545            .or_else(|| current.named_child(0))
7546        else {
7547            break;
7548        };
7549        current = child;
7550    }
7551    wrappers
7552}
7553
7554fn cpp_wrap_structured_type(
7555    identity: StructuredTypeIdentity,
7556    wrapper: CppStructuredTypeWrapper,
7557) -> Option<StructuredTypeIdentity> {
7558    match wrapper {
7559        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
7560        CppStructuredTypeWrapper::Reference => identity.wrap_reference(),
7561        CppStructuredTypeWrapper::Array => identity.wrap_array(),
7562    }
7563}
7564
7565fn cpp_wrap_structured_type_node(
7566    builder: &mut StructuredTypeIdentityBuilder,
7567    inner: StructuredTypeNodeId,
7568    wrapper: CppStructuredTypeWrapper,
7569) -> Option<StructuredTypeNodeId> {
7570    match wrapper {
7571        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
7572        CppStructuredTypeWrapper::Reference => builder.reference(inner),
7573        CppStructuredTypeWrapper::Array => builder.array(inner),
7574    }
7575}
7576
7577fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
7578    let mut path = Vec::new();
7579    let mut stack = vec![node];
7580    while let Some(current) = stack.pop() {
7581        match current.kind() {
7582            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
7583                let component = node_text(current, source).to_string();
7584                if component.is_empty() {
7585                    return None;
7586                }
7587                path.push(component);
7588            }
7589            "template_type" | "dependent_type" => {
7590                stack.push(current.child_by_field_name("name")?);
7591            }
7592            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
7593                stack.push(current.child_by_field_name("name")?);
7594                if let Some(scope) = current.child_by_field_name("scope") {
7595                    stack.push(scope);
7596                }
7597            }
7598            _ => return None,
7599        }
7600    }
7601    (!path.is_empty()).then_some(path)
7602}
7603
7604fn cpp_callable_lexical_scope<'tree>(
7605    node: Node<'tree>,
7606    source: &str,
7607    ancestry: &ParentIndex<'tree>,
7608) -> Vec<String> {
7609    let mut groups = Vec::new();
7610    let mut current = ancestry.parent(node);
7611    while let Some(parent) = current {
7612        if matches!(
7613            parent.kind(),
7614            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
7615        ) && let Some(name_node) = parent.child_by_field_name("name")
7616            && let Some(components) = cpp_structured_type_path(name_node, source)
7617            && !components.is_empty()
7618        {
7619            groups.push(components);
7620        }
7621        current = ancestry.parent(parent);
7622    }
7623    groups.reverse();
7624    groups.into_iter().flatten().collect()
7625}
7626
7627fn cpp_callable_dispatch_extensibility<'tree>(
7628    function_declarator: Node<'tree>,
7629    ancestry: &ParentIndex<'tree>,
7630) -> DispatchExtensibility {
7631    let mut declaration = None;
7632    let mut current = Some(function_declarator);
7633    while let Some(node) = current {
7634        match node.kind() {
7635            "template_declaration"
7636            | "preproc_if"
7637            | "preproc_ifdef"
7638            | "preproc_else"
7639            | "preproc_elif"
7640            | "preproc_call"
7641            | "ERROR" => return DispatchExtensibility::Open,
7642            "declaration" | "field_declaration" | "function_definition" => {
7643                declaration.get_or_insert(node);
7644            }
7645            "translation_unit" => break,
7646            _ => {}
7647        }
7648        current = ancestry.parent(node);
7649    }
7650    let Some(declaration) = declaration else {
7651        return DispatchExtensibility::Open;
7652    };
7653
7654    let mut saw_virtual_boundary = false;
7655    let mut stack = vec![declaration];
7656    while let Some(node) = stack.pop() {
7657        match node.kind() {
7658            "compound_statement" | "field_declaration_list" => continue,
7659            "final" | "final_specifier" => return DispatchExtensibility::Closed,
7660            "virtual"
7661            | "override"
7662            | "virtual_specifier"
7663            | "pure_virtual_clause"
7664            | "template_parameter_list"
7665            | "template_method"
7666            | "template_function"
7667            | "ERROR" => saw_virtual_boundary = true,
7668            _ => {}
7669        }
7670        let mut cursor = node.walk();
7671        stack.extend(node.children(&mut cursor));
7672    }
7673
7674    if saw_virtual_boundary {
7675        DispatchExtensibility::Open
7676    } else {
7677        DispatchExtensibility::Closed
7678    }
7679}
7680
7681fn cpp_callable_linkage<'tree>(
7682    declaration: Node<'tree>,
7683    source: &str,
7684    ancestry: &ParentIndex<'tree>,
7685) -> CallableLinkage {
7686    let mut enclosed_by_class = false;
7687    let mut current = ancestry.parent(declaration);
7688    while let Some(node) = current {
7689        if node.kind() == "namespace_definition"
7690            && node
7691                .child_by_field_name("name")
7692                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7693        {
7694            return CallableLinkage::Internal;
7695        }
7696        if matches!(
7697            node.kind(),
7698            "class_specifier" | "struct_specifier" | "union_specifier"
7699        ) {
7700            if node
7701                .child_by_field_name("name")
7702                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
7703            {
7704                return CallableLinkage::Internal;
7705            }
7706            enclosed_by_class = true;
7707        }
7708        if matches!(node.kind(), "function_definition" | "lambda_expression") {
7709            return CallableLinkage::Internal;
7710        }
7711        current = ancestry.parent(node);
7712    }
7713
7714    if enclosed_by_class {
7715        return CallableLinkage::External;
7716    }
7717
7718    let mut cursor = declaration.walk();
7719    if declaration.named_children(&mut cursor).any(|child| {
7720        child.kind() == "storage_class_specifier"
7721            && normalize_cpp_whitespace(node_text(child, source)) == "static"
7722    }) {
7723        CallableLinkage::Internal
7724    } else {
7725        CallableLinkage::External
7726    }
7727}
7728
7729fn cpp_callable_return_type_text<'tree>(
7730    function_declarator: Node<'tree>,
7731    source: &str,
7732    ancestry: &ParentIndex<'tree>,
7733) -> Option<String> {
7734    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
7735        return None;
7736    }
7737    if let Some((return_type, _)) =
7738        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
7739    {
7740        let text = normalize_cpp_whitespace(node_text(return_type, source));
7741        return (!text.is_empty()).then_some(text);
7742    }
7743    let mut cursor = function_declarator.walk();
7744    if let Some(trailing) = function_declarator
7745        .named_children(&mut cursor)
7746        .find(|child| child.kind() == "trailing_return_type")
7747        && let Some(type_descriptor) = trailing.named_child(0)
7748    {
7749        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
7750        if !text.is_empty() {
7751            return Some(text);
7752        }
7753    }
7754
7755    let mut current = function_declarator;
7756    let mut indirection = String::new();
7757    while let Some(parent) = ancestry.parent(current) {
7758        if matches!(
7759            parent.kind(),
7760            "function_definition" | "declaration" | "field_declaration"
7761        ) {
7762            let type_node = parent.child_by_field_name("type")?;
7763            if cpp_export_macro_token(node_text(type_node, source))
7764                && (0..parent.named_child_count()).any(|index| {
7765                    parent
7766                        .named_child(index)
7767                        .is_some_and(|child| child.kind() == "ERROR")
7768                })
7769            {
7770                // Export/decorator macros commonly occupy the grammar's `type`
7771                // field and leave the semantic return type in an ERROR sibling.
7772                // Do not persist the macro token as a return type. The malformed
7773                // declaration does not carry enough structured evidence here.
7774                return None;
7775            }
7776            let base = normalize_cpp_whitespace(node_text(type_node, source));
7777            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
7778        }
7779        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
7780            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
7781                && parent.named_child_count() == 1
7782                && parent.named_child(0) == Some(current));
7783        if wraps_current_declarator {
7784            match parent.kind() {
7785                "pointer_declarator" => indirection.push('*'),
7786                "reference_declarator" => {
7787                    let reference = parent
7788                        .children(&mut parent.walk())
7789                        .find(|child| !child.is_named())
7790                        .map(|child| node_text(child, source))
7791                        .unwrap_or("&");
7792                    indirection.push_str(reference);
7793                }
7794                "init_declarator" | "parenthesized_declarator" => {}
7795                _ => return None,
7796            }
7797            current = parent;
7798            continue;
7799        }
7800        return None;
7801    }
7802    None
7803}
7804
7805fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
7806    let mut required = 0;
7807    let mut total = 0;
7808    let mut repeated = false;
7809    let mut cursor = parameters_node.walk();
7810    for child in parameters_node.children(&mut cursor) {
7811        match child.kind() {
7812            "parameter_declaration" => {
7813                if cpp_parameter_is_explicit_object(child, source) {
7814                    continue;
7815                }
7816                if child.child_by_field_name("declarator").is_none()
7817                    && child
7818                        .child_by_field_name("type")
7819                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
7820                {
7821                    continue;
7822                }
7823                required += 1;
7824                total += 1;
7825            }
7826            "optional_parameter_declaration" => total += 1,
7827            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7828                repeated = true;
7829            }
7830            _ => {}
7831        }
7832    }
7833    CallableArity::new(required, total, repeated)
7834}
7835
7836fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
7837    parameter
7838        .child_by_field_name("type")
7839        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
7840        .and_then(|type_node| type_node.child_by_field_name("constraint"))
7841        .is_some_and(|constraint| {
7842            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
7843        })
7844}
7845
7846/// One entry of a callable's invocation parameter list.
7847///
7848/// The list excludes an explicit object parameter and a lone `void`, so its
7849/// length is the callable's invocation arity. Every derivation of a parameter
7850/// type - the rendered spelling used for overload discrimination and the
7851/// structured identity used by dependency-pack production - starts from this
7852/// same sequence, so the two can never disagree about which parameters exist.
7853#[derive(Clone, Copy)]
7854enum CppParameterSlot<'tree> {
7855    Declared(Node<'tree>),
7856    Ellipsis,
7857}
7858
7859fn cpp_callable_parameter_slots<'tree>(
7860    parameters_node: Node<'tree>,
7861    source: &str,
7862) -> Vec<CppParameterSlot<'tree>> {
7863    let mut slots = Vec::new();
7864    let mut cursor = parameters_node.walk();
7865    for parameter in parameters_node.children(&mut cursor) {
7866        match parameter.kind() {
7867            "parameter_declaration" | "optional_parameter_declaration" => {
7868                if cpp_parameter_is_explicit_object(parameter, source)
7869                    || (parameter.child_by_field_name("declarator").is_none()
7870                        && parameter
7871                            .child_by_field_name("type")
7872                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
7873                {
7874                    continue;
7875                }
7876                slots.push(CppParameterSlot::Declared(parameter));
7877            }
7878            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
7879                slots.push(CppParameterSlot::Ellipsis);
7880            }
7881            _ => {}
7882        }
7883    }
7884    slots
7885}
7886
7887fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
7888    cpp_callable_parameter_slots(parameters_node, source)
7889        .into_iter()
7890        .map(|slot| match slot {
7891            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
7892            CppParameterSlot::Ellipsis => "...".to_string(),
7893        })
7894        .collect()
7895}
7896
7897/// One callable parameter's parser-derived type.
7898///
7899/// A rendered spelling such as `const T&` is a source text, not a type name. A
7900/// consumer that must publish a type into a structured model - a semantic-pack
7901/// type reference, for example - reads this instead.
7902#[derive(Debug, Clone, PartialEq, Eq)]
7903pub enum CppParameterType {
7904    /// The written type reduced to a structured identity. C++ cv-qualifiers
7905    /// have no place in that model and are not represented.
7906    Structured(StructuredTypeIdentity),
7907    /// A `...` pack, which declares no parameter type at all.
7908    Ellipsis,
7909    /// A written type with no structured reduction, such as a macro-obscured,
7910    /// `decltype`-computed, or function-pointer parameter.
7911    Unstructured,
7912}
7913
7914/// The structured type of each invocation parameter, in declaration order.
7915///
7916/// The result is index-parallel with the rendered
7917/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
7918/// callable.
7919pub fn cpp_callable_parameter_type_identities<'tree>(
7920    function_declarator: Node<'tree>,
7921    source: &str,
7922    ancestry: &ParentIndex<'tree>,
7923) -> Vec<CppParameterType> {
7924    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
7925        return Vec::new();
7926    };
7927    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
7928    cpp_callable_parameter_slots(parameters_node, source)
7929        .into_iter()
7930        .map(|slot| match slot {
7931            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
7932            CppParameterSlot::Declared(parameter) => {
7933                cpp_parameter_type_identity(parameter, source, &lexical_scope)
7934                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
7935            }
7936        })
7937        .collect()
7938}
7939
7940fn cpp_parameter_type_identity(
7941    parameter: Node<'_>,
7942    source: &str,
7943    lexical_scope: &[String],
7944) -> Option<StructuredTypeIdentity> {
7945    let type_node = parameter.child_by_field_name("type")?;
7946    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
7947    if let Some(declarator) = cpp_parameter_declarator(parameter) {
7948        for wrapper in cpp_structured_declarator_wrappers(declarator)
7949            .into_iter()
7950            .rev()
7951        {
7952            identity = cpp_wrap_structured_type(identity, wrapper)?;
7953        }
7954    }
7955    Some(identity)
7956}
7957
7958/// One callable parameter's comparable shape.
7959///
7960/// [`CppParameterType`] above answers "which type is written here" for a
7961/// structured model and deliberately records no cv-qualifiers, so it reports
7962/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
7963/// callable declarations declare one function needs the opposite trade: every
7964/// cv-qualifier that C++ counts as part of the parameter type must survive,
7965/// while the two declarations may spell the same type through different
7966/// qualifications. This slot carries that comparand.
7967///
7968/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
7969/// and with the rendered parameter spellings of the same callable.
7970#[derive(Debug, Clone, PartialEq, Eq)]
7971pub enum CppComparableSlot {
7972    /// A declared parameter reduced to its comparable shape.
7973    Shape(CppComparableParameter),
7974    /// A `...` pack, which declares no parameter type at all.
7975    Ellipsis,
7976    /// A parameter with no comparable reduction, such as a macro-obscured,
7977    /// `decltype`-computed, or function-pointer parameter.
7978    Unstructured,
7979}
7980
7981/// A parameter type as a flat arena of nodes plus a root index.
7982///
7983/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
7984/// can nest types very deeply, and cloning, comparing or dropping the value
7985/// must not consume the Rust call stack. Nodes are appended in post-order, so
7986/// every child index is smaller than its parent's and the last appended node is
7987/// the root.
7988///
7989/// That post-order append is also what makes the derived `PartialEq` a correct
7990/// structural equality: the builder below is deterministic, so one type shape
7991/// has exactly one arena layout no matter which spelling produced it. Two
7992/// shapes are equal as values iff they are equal as type trees.
7993#[derive(Debug, Clone, PartialEq, Eq)]
7994pub struct CppComparableParameter {
7995    nodes: Vec<CppComparableNode>,
7996    root: usize,
7997}
7998
7999/// One node of a [`CppComparableParameter`] arena.
8000///
8001/// `Reference` and `Array` carry no qualifiers because the grammar writes none
8002/// on them: a reference cannot be cv-qualified in C++, and an array's
8003/// qualifiers belong to its element type. A cv-qualifier written on a generic
8004/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
8005/// which is the only Named node the whole spelling produces.
8006#[derive(Debug, Clone, PartialEq, Eq)]
8007pub enum CppComparableNode {
8008    Named {
8009        name: StructuredTypeName,
8010        primitive: bool,
8011        konst: bool,
8012        volatil: bool,
8013    },
8014    Pointer {
8015        inner: usize,
8016        konst: bool,
8017        volatil: bool,
8018    },
8019    Reference {
8020        inner: usize,
8021    },
8022    Array {
8023        inner: usize,
8024    },
8025    Generic {
8026        base: usize,
8027        arguments: Vec<usize>,
8028    },
8029}
8030
8031impl CppComparableParameter {
8032    pub fn root(&self) -> usize {
8033        self.root
8034    }
8035
8036    pub fn node(&self, index: usize) -> &CppComparableNode {
8037        &self.nodes[index]
8038    }
8039
8040    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
8041    /// parameter's top level only.
8042    ///
8043    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
8044    /// declare one function, and a top-level array type becomes a pointer to
8045    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
8046    /// type constructor is this arena's root, which is why both adjustments
8047    /// are one match on it: cv on an inner pointer level, on a pointee, or on
8048    /// an array element keeps distinguishing the type, and an array behind a
8049    /// pointer or reference is not a top-level array.
8050    fn adjust_parameter_top_level(&mut self) {
8051        let root = self.root;
8052        match &mut self.nodes[root] {
8053            CppComparableNode::Named { konst, volatil, .. }
8054            | CppComparableNode::Pointer { konst, volatil, .. } => {
8055                *konst = false;
8056                *volatil = false;
8057            }
8058            CppComparableNode::Array { inner } => {
8059                let inner = *inner;
8060                self.nodes[root] = CppComparableNode::Pointer {
8061                    inner,
8062                    konst: false,
8063                    volatil: false,
8064                };
8065            }
8066            CppComparableNode::Generic { base, .. } => {
8067                let base = *base;
8068                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
8069                    unreachable!("a comparable generic's base is always a named leaf");
8070                };
8071                *konst = false;
8072                *volatil = false;
8073            }
8074            CppComparableNode::Reference { .. } => {}
8075        }
8076    }
8077}
8078
8079/// The comparable shape of each invocation parameter, in declaration order.
8080///
8081/// The result is index-parallel with
8082/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
8083/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
8084/// must treat as evidence of nothing rather than as agreement.
8085pub fn cpp_comparable_parameter_shapes<'tree>(
8086    function_declarator: Node<'tree>,
8087    source: &str,
8088    ancestry: &ParentIndex<'tree>,
8089) -> Vec<CppComparableSlot> {
8090    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
8091        return Vec::new();
8092    };
8093    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
8094    cpp_callable_parameter_slots(parameters_node, source)
8095        .into_iter()
8096        .map(|slot| match slot {
8097            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
8098            CppParameterSlot::Declared(parameter) => {
8099                cpp_comparable_parameter(parameter, source, &lexical_scope)
8100                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
8101            }
8102        })
8103        .collect()
8104}
8105
8106fn cpp_comparable_parameter(
8107    parameter: Node<'_>,
8108    source: &str,
8109    lexical_scope: &[String],
8110) -> Option<CppComparableParameter> {
8111    let type_node = parameter.child_by_field_name("type")?;
8112    let levels = match cpp_parameter_declarator(parameter) {
8113        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8114        None => Vec::new(),
8115    };
8116    let mut shape = cpp_comparable_type_shape(
8117        type_node,
8118        cpp_cv_qualifiers(parameter, source),
8119        levels,
8120        source,
8121        lexical_scope,
8122    )?;
8123    shape.adjust_parameter_top_level();
8124    Some(shape)
8125}
8126
8127/// The `const` and `volatile` qualifiers written as direct named children of
8128/// `node`.
8129///
8130/// The grammar exposes `type_qualifier` as a non-field named child in exactly
8131/// the three places a parameter's qualifiers can be written: on the
8132/// `parameter_declaration` itself (the base type), on a `type_descriptor`
8133/// (inside a template argument list), and on each `pointer_declarator` level
8134/// (the pointer object). Every other qualifier the grammar admits - `restrict`
8135/// and friends - takes no part in C++ type identity, the same filter
8136/// `cpp_parameter_type` applies to the rendered spelling (#1827).
8137fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
8138    let mut qualifiers = CppCvQualifiers::default();
8139    let mut cursor = node.walk();
8140    for child in node.named_children(&mut cursor) {
8141        if child.kind() != "type_qualifier" {
8142            continue;
8143        }
8144        match node_text(child, source) {
8145            "const" => qualifiers.konst = true,
8146            "volatile" => qualifiers.volatil = true,
8147            _ => {}
8148        }
8149    }
8150    qualifiers
8151}
8152
8153#[derive(Clone, Copy, Default)]
8154struct CppCvQualifiers {
8155    konst: bool,
8156    volatil: bool,
8157}
8158
8159impl CppCvQualifiers {
8160    fn union(self, other: Self) -> Self {
8161        Self {
8162            konst: self.konst || other.konst,
8163            volatil: self.volatil || other.volatil,
8164        }
8165    }
8166}
8167
8168/// One pointer, reference or array level a declarator chain adds.
8169#[derive(Clone, Copy)]
8170enum CppComparableLevel {
8171    Pointer { konst: bool, volatil: bool },
8172    Reference,
8173    Array,
8174}
8175
8176/// The levels `declarator` adds, outermost written level first.
8177///
8178/// C++ declarator syntax binds inside out: the level written closest to the
8179/// declared name is the outermost type constructor, and tree-sitter nests it
8180/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
8181/// applies in order to reach "array of pointer to int", and the qualifier of
8182/// `int * const *p` is read on the level it was written next to, the inner
8183/// pointer of the resulting type.
8184///
8185/// A declarator chain that names a function type - a function-pointer
8186/// parameter - has no comparable shape and reports `None`, matching the
8187/// structured identity channel.
8188fn cpp_comparable_declarator_levels(
8189    declarator: Node<'_>,
8190    source: &str,
8191) -> Option<Vec<CppComparableLevel>> {
8192    let mut levels = Vec::new();
8193    let mut current = declarator;
8194    loop {
8195        match current.kind() {
8196            "pointer_declarator" | "abstract_pointer_declarator" => {
8197                let qualifiers = cpp_cv_qualifiers(current, source);
8198                levels.push(CppComparableLevel::Pointer {
8199                    konst: qualifiers.konst,
8200                    volatil: qualifiers.volatil,
8201                });
8202            }
8203            "reference_declarator" | "abstract_reference_declarator" => {
8204                levels.push(CppComparableLevel::Reference);
8205            }
8206            "array_declarator" | "abstract_array_declarator" => {
8207                levels.push(CppComparableLevel::Array);
8208            }
8209            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
8210            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
8211            _ => return None,
8212        }
8213        let Some(next) = cpp_nested_declarator(current) else {
8214            return Some(levels);
8215        };
8216        current = next;
8217    }
8218}
8219
8220/// Reduce one written type to a comparable arena.
8221///
8222/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
8223/// two additions: each visited type node carries the cv-qualifiers written on
8224/// it, and declarator levels arrive as a prepared list rather than being
8225/// rediscovered inside the walk.
8226fn cpp_comparable_type_shape(
8227    type_node: Node<'_>,
8228    qualifiers: CppCvQualifiers,
8229    levels: Vec<CppComparableLevel>,
8230    source: &str,
8231    lexical_scope: &[String],
8232) -> Option<CppComparableParameter> {
8233    enum Work<'tree> {
8234        Visit {
8235            node: Node<'tree>,
8236            qualifiers: CppCvQualifiers,
8237        },
8238        ApplyLevels(Vec<CppComparableLevel>),
8239        BuildGeneric {
8240            argument_count: usize,
8241        },
8242    }
8243
8244    let mut nodes: Vec<CppComparableNode> = Vec::new();
8245    let mut values: Vec<usize> = Vec::new();
8246    let mut work = vec![
8247        Work::ApplyLevels(levels),
8248        Work::Visit {
8249            node: type_node,
8250            qualifiers,
8251        },
8252    ];
8253    while let Some(next) = work.pop() {
8254        match next {
8255            Work::Visit { node, qualifiers } => match node.kind() {
8256                "type_descriptor" => {
8257                    let inner_type = node
8258                        .child_by_field_name("type")
8259                        .or_else(|| node.named_child(0))?;
8260                    let mut cursor = node.walk();
8261                    let declarator = node.child_by_field_name("declarator").or_else(|| {
8262                        node.named_children(&mut cursor).find(|child| {
8263                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
8264                        })
8265                    });
8266                    let levels = match declarator {
8267                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
8268                        None => Vec::new(),
8269                    };
8270                    work.push(Work::ApplyLevels(levels));
8271                    work.push(Work::Visit {
8272                        node: inner_type,
8273                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
8274                    });
8275                }
8276                "sized_type_specifier" => {
8277                    // `unsigned char` is one primitive type whose components are
8278                    // partly unnamed tokens, so the whole specifier is its own
8279                    // name component. Reducing it to the `type` child would make
8280                    // `f(unsigned char)` and `f(char)` compare equal.
8281                    let name = StructuredTypeName::new(
8282                        vec![normalize_cpp_whitespace(node_text(node, source))],
8283                        lexical_scope.to_vec(),
8284                        false,
8285                    )?;
8286                    values.push(cpp_push_comparable_node(
8287                        &mut nodes,
8288                        CppComparableNode::Named {
8289                            name,
8290                            primitive: true,
8291                            konst: qualifiers.konst,
8292                            volatil: qualifiers.volatil,
8293                        },
8294                    ));
8295                }
8296                "qualified_identifier"
8297                | "scoped_identifier"
8298                | "scoped_type_identifier"
8299                | "type_identifier"
8300                | "field_identifier"
8301                | "identifier"
8302                | "namespace_identifier"
8303                | "primitive_type"
8304                | "template_type" => {
8305                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
8306                    values.push(cpp_push_comparable_node(
8307                        &mut nodes,
8308                        CppComparableNode::Named {
8309                            name,
8310                            primitive: node.kind() == "primitive_type",
8311                            konst: qualifiers.konst,
8312                            volatil: qualifiers.volatil,
8313                        },
8314                    ));
8315                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
8316                        let mut cursor = arguments_node.walk();
8317                        let arguments = arguments_node
8318                            .named_children(&mut cursor)
8319                            .filter(|child| !child.is_extra() && child.kind() != "comment")
8320                            .collect::<Vec<_>>();
8321                        work.push(Work::BuildGeneric {
8322                            argument_count: arguments.len(),
8323                        });
8324                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
8325                            node: argument,
8326                            qualifiers: CppCvQualifiers::default(),
8327                        }));
8328                    }
8329                }
8330                _ => {
8331                    let inner = node.child_by_field_name("type").or_else(|| {
8332                        (node.named_child_count() == 1)
8333                            .then(|| node.named_child(0))
8334                            .flatten()
8335                    })?;
8336                    work.push(Work::Visit {
8337                        node: inner,
8338                        qualifiers,
8339                    });
8340                }
8341            },
8342            Work::ApplyLevels(levels) => {
8343                let mut root = values.pop()?;
8344                for level in levels {
8345                    let node = match level {
8346                        CppComparableLevel::Pointer { konst, volatil } => {
8347                            CppComparableNode::Pointer {
8348                                inner: root,
8349                                konst,
8350                                volatil,
8351                            }
8352                        }
8353                        CppComparableLevel::Reference => {
8354                            CppComparableNode::Reference { inner: root }
8355                        }
8356                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
8357                    };
8358                    root = cpp_push_comparable_node(&mut nodes, node);
8359                }
8360                values.push(root);
8361            }
8362            Work::BuildGeneric { argument_count } => {
8363                let value_count = argument_count.checked_add(1)?;
8364                let start = values.len().checked_sub(value_count)?;
8365                let mut built = values.split_off(start);
8366                let base = built.remove(0);
8367                values.push(cpp_push_comparable_node(
8368                    &mut nodes,
8369                    CppComparableNode::Generic {
8370                        base,
8371                        arguments: built,
8372                    },
8373                ));
8374            }
8375        }
8376    }
8377    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
8378    debug_assert_eq!(
8379        root,
8380        nodes.len().saturating_sub(1),
8381        "comparable nodes are appended in post-order, so the root is the last one"
8382    );
8383    Some(CppComparableParameter { nodes, root })
8384}
8385
8386fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
8387    nodes.push(node);
8388    nodes.len() - 1
8389}
8390
8391/// The template argument list of the name `node` terminates in, if any.
8392///
8393/// `std::vector<int>` writes its arguments on the `name` of a qualified
8394/// identifier, so a walk that stopped at the qualified node would reduce
8395/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
8396fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
8397    let mut current = node;
8398    loop {
8399        match current.kind() {
8400            "template_type" => return current.child_by_field_name("arguments"),
8401            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
8402                current = current.child_by_field_name("name")?;
8403            }
8404            _ => return None,
8405        }
8406    }
8407}
8408
8409/// The callable declarator of the declaration that covers `start_byte`.
8410///
8411/// A consumer that holds a declaration's recorded byte position rather than its
8412/// syntax node - external header extraction, for instance - uses this to reach
8413/// the same `function_declarator` the declaration walk read.
8414pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
8415    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
8416    loop {
8417        if matches!(
8418            current.kind(),
8419            "declaration" | "field_declaration" | "function_definition"
8420        ) && let Some(declarator) = current
8421            .child_by_field_name("declarator")
8422            .and_then(extract_function_declarator)
8423        {
8424            return Some(declarator);
8425        }
8426        current = current.parent()?;
8427    }
8428}
8429
8430fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
8431    let mut labels = Vec::new();
8432    let mut cursor = parameters_node.walk();
8433    for child in parameters_node.children(&mut cursor) {
8434        match child.kind() {
8435            "parameter_declaration" | "optional_parameter_declaration" => {
8436                if let Some(name_node) = child
8437                    .child_by_field_name("declarator")
8438                    .and_then(cpp_declarator_label_node)
8439                {
8440                    labels.push(name_node);
8441                } else {
8442                    labels.push(child);
8443                }
8444            }
8445            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
8446                labels.push(child);
8447            }
8448            _ => {}
8449        }
8450    }
8451    labels
8452}
8453
8454fn cpp_signature_search_start<'tree>(
8455    signature: &str,
8456    function_declarator: Node<'tree>,
8457    source: &str,
8458    ancestry: &ParentIndex<'tree>,
8459) -> usize {
8460    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
8461        return 0;
8462    };
8463    let raw = node_text(enclosing, source);
8464    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
8465    let offset = function_declarator
8466        .start_byte()
8467        .saturating_sub(enclosing.start_byte())
8468        .saturating_sub(leading_trim_bytes);
8469    offset.min(signature.len())
8470}
8471
8472fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
8473    match node.kind() {
8474        "identifier" | "field_identifier" => Some(node),
8475        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
8476            .child_by_field_name("declarator")
8477            .or_else(|| last_named_child(node))
8478            .and_then(cpp_declarator_label_node),
8479        "array_declarator" => node
8480            .child_by_field_name("declarator")
8481            .and_then(cpp_declarator_label_node),
8482        "function_declarator" => node
8483            .child_by_field_name("declarator")
8484            .or_else(|| node.child_by_field_name("name"))
8485            .or_else(|| last_named_child(node))
8486            .and_then(cpp_declarator_label_node),
8487        _ => None,
8488    }
8489}
8490
8491fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
8492    let base_type = parameter
8493        .child_by_field_name("type")
8494        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
8495        .unwrap_or_default();
8496    let declarator = cpp_parameter_declarator(parameter);
8497    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
8498    // are discarded, so `f(const int)` and `f(int)` declare one function. A
8499    // qualifier written next to the parameter's type is only top-level when
8500    // the declarator adds no indirection; behind a pointer, reference or array
8501    // declarator the same qualifier belongs to the pointee, referent or
8502    // element and keeps distinguishing the type (#1827).
8503    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
8504    let mut cursor = parameter.walk();
8505    let qualifiers = parameter
8506        .named_children(&mut cursor)
8507        .filter(|child| child.kind() == "type_qualifier")
8508        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8509        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
8510        .collect::<Vec<_>>()
8511        .join(" ");
8512    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
8513        (true, _) => base_type,
8514        (_, true) => qualifiers,
8515        (false, false) => format!("{qualifiers} {base_type}"),
8516    };
8517    let declarator_suffix = declarator
8518        .map(|node| cpp_declarator_suffix_without_name(node, source))
8519        .unwrap_or_default();
8520
8521    let combined = if type_text.is_empty() {
8522        declarator_suffix
8523    } else if declarator_suffix.is_empty() {
8524        type_text
8525    } else {
8526        format!("{type_text} {declarator_suffix}")
8527    };
8528    normalize_cpp_type_text(&combined)
8529}
8530
8531fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
8532    parameter.child_by_field_name("declarator").or_else(|| {
8533        // Some unnamed prototype parameters expose their abstract declarator
8534        // as a direct named child without the grammar's `declarator` field.
8535        // Recover only the structured abstract-declarator node; the parameter's
8536        // type and qualifiers are distinct children and must not be guessed from
8537        // source text.
8538        let mut cursor = parameter.walk();
8539        parameter
8540            .named_children(&mut cursor)
8541            .find(|child| is_cpp_abstract_declarator(child.kind()))
8542    })
8543}
8544
8545/// Whether a parameter's declarator chain adds indirection - a pointer,
8546/// reference, array or function declarator - to the parameter's written type.
8547pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
8548    let mut current = Some(declarator);
8549    while let Some(node) = current {
8550        if matches!(
8551            node.kind(),
8552            "pointer_declarator"
8553                | "abstract_pointer_declarator"
8554                | "reference_declarator"
8555                | "abstract_reference_declarator"
8556                | "array_declarator"
8557                | "abstract_array_declarator"
8558                | "function_declarator"
8559                | "abstract_function_declarator"
8560        ) {
8561            return true;
8562        }
8563        current = cpp_nested_declarator(node);
8564    }
8565    false
8566}
8567
8568fn is_cpp_abstract_declarator(kind: &str) -> bool {
8569    matches!(
8570        kind,
8571        "abstract_pointer_declarator"
8572            | "abstract_reference_declarator"
8573            | "abstract_array_declarator"
8574            | "abstract_function_declarator"
8575            | "abstract_parenthesized_declarator"
8576    )
8577}
8578
8579fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
8580    node.child_by_field_name("declarator").or_else(|| {
8581        if is_cpp_abstract_declarator(node.kind()) {
8582            let mut cursor = node.walk();
8583            node.named_children(&mut cursor)
8584                .find(|child| is_cpp_abstract_declarator(child.kind()))
8585        } else {
8586            // Named declarators historically use their last named child when
8587            // tree-sitter omits the field. Keep that broad fallback for
8588            // attributed, variadic, and recovered named shapes.
8589            last_named_child(node)
8590        }
8591    })
8592}
8593
8594fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
8595    match node.kind() {
8596        "identifier" | "field_identifier" => String::new(),
8597        "pointer_declarator" | "abstract_pointer_declarator" => {
8598            let inner = cpp_nested_declarator(node)
8599                .map(|child| cpp_declarator_suffix_without_name(child, source))
8600                .unwrap_or_default();
8601            format!("*{inner}")
8602        }
8603        "reference_declarator" | "abstract_reference_declarator" => {
8604            let inner = cpp_nested_declarator(node)
8605                .map(|child| cpp_declarator_suffix_without_name(child, source))
8606                .unwrap_or_default();
8607            let reference = node
8608                .children(&mut node.walk())
8609                .find(|child| matches!(child.kind(), "&" | "&&"))
8610                .map(|child| node_text(child, source))
8611                .unwrap_or("&");
8612            format!("{reference}{inner}")
8613        }
8614        "array_declarator" | "abstract_array_declarator" => {
8615            let inner = cpp_nested_declarator(node)
8616                .map(|child| cpp_declarator_suffix_without_name(child, source))
8617                .unwrap_or_default();
8618            let size = node
8619                .child_by_field_name("size")
8620                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
8621                .unwrap_or_default();
8622            format!("{inner}[{size}]")
8623        }
8624        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
8625            let inner = cpp_nested_declarator(node);
8626            inner
8627                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
8628                .unwrap_or_default()
8629        }
8630        "function_declarator" | "abstract_function_declarator" => {
8631            let inner = cpp_nested_declarator(node)
8632                .map(|child| cpp_declarator_suffix_without_name(child, source))
8633                .unwrap_or_default();
8634            let params = node
8635                .child_by_field_name("parameters")
8636                .map(|child| cpp_parameter_signature(child, source))
8637                .unwrap_or_else(|| "()".to_string());
8638            format!("{inner}{params}")
8639        }
8640        _ => {
8641            let text = normalize_cpp_whitespace(node_text(node, source));
8642            let name = extract_declarator_name(node, source);
8643            if name.is_empty() {
8644                text
8645            } else {
8646                text.replace(&name, "").trim().to_string()
8647            }
8648        }
8649    }
8650}
8651
8652fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
8653    collapse_cpp_whitespace(
8654        suffix
8655            .trim()
8656            .trim_start_matches("->")
8657            .trim_start_matches('{')
8658            .trim_end_matches(';'),
8659    )
8660}
8661
8662pub fn normalize_cpp_whitespace(value: &str) -> String {
8663    collapse_cpp_whitespace(value)
8664}
8665
8666fn normalize_cpp_type_text(value: &str) -> String {
8667    collapse_cpp_whitespace(value)
8668        .replace(", ", ",")
8669        .replace(" <", "<")
8670        .replace("< ", "<")
8671        .replace(" >", ">")
8672}
8673
8674fn collapse_cpp_whitespace(value: &str) -> String {
8675    let mut result = String::new();
8676    let mut prev_space = false;
8677    for ch in value.chars() {
8678        if ch.is_whitespace() {
8679            if !prev_space {
8680                result.push(' ');
8681            }
8682            prev_space = true;
8683        } else {
8684            result.push(ch);
8685            prev_space = false;
8686        }
8687    }
8688    result.trim().to_string()
8689}
8690
8691pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
8692    node_source_text(node, source)
8693}
8694
8695pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
8696    walk_named_tree_preorder(node, true, |node| {
8697        match node.kind() {
8698            "type_identifier" | "identifier" | "qualified_identifier" => {
8699                let text = node_text(node, source).trim();
8700                if !text.is_empty() {
8701                    identifiers.insert(text.to_string());
8702                }
8703            }
8704            _ => {}
8705        }
8706        WalkControl::Continue
8707    });
8708}
8709
8710fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
8711    node.child_by_field_name("body").or_else(|| {
8712        let mut cursor = node.walk();
8713        node.named_children(&mut cursor).find(|child| {
8714            matches!(
8715                child.kind(),
8716                "declaration_list" | "field_declaration_list" | "enumerator_list"
8717            )
8718        })
8719    })
8720}
8721
8722/// Return a class body's actual closing brace when the parser supplied one.
8723///
8724/// A malformed namespace sentinel can leave a class node carrying unrelated
8725/// parser errors even though its own class body is complete.  `has_error()` is
8726/// therefore too coarse an admission predicate for sentinel ownership.  The
8727/// body list, however, exposes the opening and closing punctuation directly;
8728/// a real (non-missing) final `}` proves that the class did not borrow the
8729/// enclosing namespace's close.  Requiring the body to end before its parent
8730/// container also rejects a recovered node whose body swallowed that outer
8731/// boundary.
8732fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
8733    if !matches!(
8734        node.kind(),
8735        "class_specifier" | "struct_specifier" | "union_specifier"
8736    ) {
8737        return None;
8738    }
8739    let body = cpp_body_node(node)?;
8740    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
8741        return None;
8742    }
8743    let open = body.child(0)?;
8744    let close = body.child(body.child_count().checked_sub(1)?)?;
8745    if open.kind() != "{"
8746        || open.is_missing()
8747        || close.kind() != "}"
8748        || close.is_missing()
8749        || close.end_byte() != body.end_byte()
8750        || body.end_byte() > node.end_byte()
8751        || node
8752            .parent()
8753            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
8754    {
8755        return None;
8756    }
8757    Some(close)
8758}
8759
8760fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
8761    if node.kind() == "namespace_definition" {
8762        return true;
8763    }
8764    let mut cursor = node.walk();
8765    node.named_children(&mut cursor)
8766        .any(cpp_contains_namespace_definition)
8767}
8768
8769struct CppNestedNamespaceSentinel<'tree> {
8770    function: Node<'tree>,
8771    body: Node<'tree>,
8772    namespace_components: Vec<String>,
8773}
8774
8775/// Owned structural recovery metadata for a namespace-sentinel region.
8776///
8777/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
8778/// instead of the namespace/class scopes that the declaration visitor restores.
8779/// The inverted usage walk has the original CST, so it needs the same ownership
8780/// evidence without borrowing parser nodes across its file scan.  Keep this
8781/// descriptor deliberately source-range based: callers can match a reference
8782/// node by containment and then resolve its structured type spelling in the
8783/// recovered class scope.
8784#[derive(Debug, Clone)]
8785pub struct CppSentinelRecoveredOwner {
8786    pub range: Range,
8787    /// Start of the qualified owner name (`btree<P>::method`).  A leading
8788    /// return type before this byte is looked up from the namespace; parameters,
8789    /// trailing returns, and the body use the member owner scope.
8790    pub owner_name_start_byte: usize,
8791    /// Number of leading components belonging to the namespace rather than
8792    /// the qualified class owner.  A leading return type is looked up before
8793    /// every owner component, not merely before the innermost class.
8794    pub namespace_component_count: usize,
8795    pub scope_components: Vec<String>,
8796}
8797
8798#[derive(Debug, Clone)]
8799pub struct CppSentinelRecoveredClass {
8800    pub namespace_range: Range,
8801    pub namespace_scope_components: Vec<String>,
8802    pub class_range: Range,
8803    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
8804    pub scope_components: Vec<String>,
8805    /// Qualified out-of-line member definitions owned by this class.  Their
8806    /// ranges may extend beyond `class_range` when the malformed sentinel
8807    /// swallowed the namespace close and left definitions as function siblings.
8808    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
8809}
8810
8811/// Resolve the lexical scope restored for a node in a malformed
8812/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
8813/// outrank class spans, which in turn outrank the surviving namespace body.
8814/// The class ancestor suffix is recovered from the original CST so nested
8815/// members keep their complete `Outer::Inner` owner chain.
8816pub fn cpp_sentinel_recovered_scope_for_node(
8817    node: Node<'_>,
8818    source: &str,
8819    recovered_classes: &[CppSentinelRecoveredClass],
8820) -> Option<Vec<String>> {
8821    let contains =
8822        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
8823    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
8824    for recovered in recovered_classes {
8825        for owner in recovered
8826            .owner_ranges
8827            .iter()
8828            .filter(|owner| contains(owner.range))
8829        {
8830            let replace = best_owner.is_none_or(|existing| {
8831                owner.range.end_byte.saturating_sub(owner.range.start_byte)
8832                    < existing
8833                        .range
8834                        .end_byte
8835                        .saturating_sub(existing.range.start_byte)
8836            });
8837            if replace {
8838                best_owner = Some(owner);
8839            }
8840        }
8841    }
8842    if let Some(owner) = best_owner {
8843        let mut scope = owner.scope_components.clone();
8844        if node.start_byte() < owner.owner_name_start_byte {
8845            scope.truncate(owner.namespace_component_count);
8846        }
8847        return Some(scope);
8848    }
8849
8850    let class = recovered_classes
8851        .iter()
8852        .filter(|recovered| contains(recovered.class_range))
8853        .min_by_key(|recovered| {
8854            recovered
8855                .class_range
8856                .end_byte
8857                .saturating_sub(recovered.class_range.start_byte)
8858        });
8859    let class_scope = class.is_some();
8860    let mut scope = if let Some(class) = class {
8861        class.scope_components.clone()
8862    } else {
8863        let namespace = recovered_classes
8864            .iter()
8865            .filter(|recovered| contains(recovered.namespace_range))
8866            .min_by_key(|recovered| {
8867                recovered
8868                    .namespace_range
8869                    .end_byte
8870                    .saturating_sub(recovered.namespace_range.start_byte)
8871            })?;
8872        let mut scope = namespace.namespace_scope_components.clone();
8873        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
8874        let common_prefix = scope
8875            .iter()
8876            .zip(&parser_namespace)
8877            .take_while(|(recovered, parser)| recovered == parser)
8878            .count();
8879        scope.extend(parser_namespace.into_iter().skip(common_prefix));
8880        scope
8881    };
8882    if class_scope {
8883        let mut ancestor_components = Vec::new();
8884        let mut ancestor = node.parent();
8885        while let Some(current) = ancestor {
8886            if matches!(
8887                current.kind(),
8888                "class_specifier" | "struct_specifier" | "union_specifier"
8889            ) && let Some(name) = current.child_by_field_name("name")
8890                && let Some(name_components) = cpp_name_components(name, source)
8891            {
8892                ancestor_components.push(
8893                    name_components
8894                        .into_iter()
8895                        .map(|component| component.name)
8896                        .collect::<Vec<_>>(),
8897                );
8898            }
8899            ancestor = current.parent();
8900        }
8901        ancestor_components.reverse();
8902        let base_len = scope.len();
8903        for component in ancestor_components.into_iter().flatten() {
8904            if scope.len() >= base_len && scope.last() == Some(&component) {
8905                continue;
8906            }
8907            scope.push(component);
8908        }
8909    }
8910    Some(scope)
8911}
8912
8913struct CppSentinelFragmentedClassTail<'tree> {
8914    class_node: Node<'tree>,
8915    template_node: Option<Node<'tree>>,
8916    name: String,
8917    raw_supertypes: Option<Vec<String>>,
8918    fragmented: FragmentedExportBody,
8919    consumed_start: usize,
8920}
8921
8922struct CppSentinelFragmentedClassErrorPrefix<'tree> {
8923    name: String,
8924    open: Node<'tree>,
8925    raw_supertypes: Option<Vec<String>>,
8926}
8927
8928struct CppSentinelDirectBodyClassRegion {
8929    namespace_components: Vec<String>,
8930    class_start: usize,
8931    class_start_line: usize,
8932    class_close_end: usize,
8933    class_close_line: usize,
8934    name: String,
8935}
8936
8937fn cpp_sentinel_body_class_candidate<'tree>(
8938    child: Node<'tree>,
8939) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
8940    if matches!(
8941        child.kind(),
8942        "class_specifier" | "struct_specifier" | "union_specifier"
8943    ) {
8944        return Some((child, None));
8945    }
8946    if child.kind() != "template_declaration" {
8947        if child.kind() == "declaration" {
8948            return Some((first_class_like_child(child)?, None));
8949        }
8950        return None;
8951    }
8952    let mut cursor = child.walk();
8953    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
8954        if matches!(
8955            candidate.kind(),
8956            "class_specifier" | "struct_specifier" | "union_specifier"
8957        ) {
8958            Some(candidate)
8959        } else if candidate.kind() == "declaration" {
8960            first_class_like_child(candidate)
8961        } else {
8962            None
8963        }
8964    })?;
8965    Some((class_node, Some(child)))
8966}
8967
8968/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
8969/// namespace-sentinel body when a later member macro ends the bogus sentinel
8970/// function before the real class close. The anonymous class/open tokens and
8971/// direct identifier are the structural proof; a retained direct close would
8972/// be an ordinary malformed class rather than the fragmented tail handled here.
8973fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
8974    node: Node<'tree>,
8975    source: &str,
8976) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
8977    let name = malformed_class_error_owner_name(node, source)?;
8978    let mut cursor = node.walk();
8979    let children = node.children(&mut cursor).collect::<Vec<_>>();
8980    let keyword = children.first()?;
8981    let open_index = children.iter().position(|child| child.kind() == "{")?;
8982    if children[open_index + 1..]
8983        .iter()
8984        .any(|child| child.kind() == "}")
8985    {
8986        return None;
8987    }
8988    let raw_supertypes =
8989        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
8990    Some(CppSentinelFragmentedClassErrorPrefix {
8991        name,
8992        open: children[open_index],
8993        raw_supertypes,
8994    })
8995}
8996
8997fn cpp_sentinel_direct_body_class_candidate<'tree>(
8998    child: Node<'tree>,
8999) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
9000    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
9001        return Some(candidate);
9002    }
9003    if child.kind() != "template_declaration" {
9004        return None;
9005    }
9006    let mut cursor = child.walk();
9007    let wrapper = child
9008        .named_children(&mut cursor)
9009        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
9010    Some((first_class_like_child(wrapper)?, Some(child)))
9011}
9012
9013fn cpp_sentinel_direct_namespace_components(
9014    function: Node<'_>,
9015    body: Node<'_>,
9016    source: &str,
9017) -> Option<Vec<String>> {
9018    let mut cursor = function.walk();
9019    let children = function
9020        .named_children(&mut cursor)
9021        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
9022        .collect::<Vec<_>>();
9023    let sentinel_index = children.iter().rposition(|child| {
9024        direct_identifier_name(*child, source)
9025            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
9026    })?;
9027    let mut identifiers = Vec::new();
9028    let mut stack = children[sentinel_index + 1..]
9029        .iter()
9030        .rev()
9031        .copied()
9032        .collect::<Vec<_>>();
9033    while let Some(current) = stack.pop() {
9034        if let Some(name) = direct_identifier_name(current, source) {
9035            identifiers.push(name);
9036            continue;
9037        }
9038        let mut cursor = current.walk();
9039        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
9040        stack.extend(children.into_iter().rev());
9041    }
9042    let [keyword, namespace] = identifiers.as_slice() else {
9043        return None;
9044    };
9045    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
9046        .then(|| vec![namespace.clone()])
9047}
9048
9049fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
9050    let mut sibling = class_semicolon.next_named_sibling();
9051    let namespace_close = loop {
9052        let Some(current) = sibling else {
9053            return false;
9054        };
9055        sibling = current.next_named_sibling();
9056        if current.kind() != "comment" {
9057            break current;
9058        }
9059    };
9060    if !cpp_is_stray_close_brace(namespace_close, source) {
9061        return false;
9062    }
9063    loop {
9064        let Some(current) = sibling else {
9065            return false;
9066        };
9067        sibling = current.next_named_sibling();
9068        if current.kind() == "comment" {
9069            continue;
9070        }
9071        return direct_identifier_name(current, source)
9072            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
9073    }
9074}
9075
9076fn cpp_sentinel_macro_body_class_region<'tree>(
9077    node: Node<'tree>,
9078    source: &str,
9079    ancestry: &ParentIndex<'tree>,
9080) -> Option<CppSentinelDirectBodyClassRegion> {
9081    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
9082        return None;
9083    };
9084    if node.kind() != "function_definition" || !node.has_error() {
9085        return None;
9086    }
9087    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
9088    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
9089    let mut cursor = body.walk();
9090    let candidates = body
9091        .named_children(&mut cursor)
9092        .filter_map(cpp_sentinel_direct_body_class_candidate)
9093        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
9094        .collect::<Vec<_>>();
9095    let [(class_node, template_node)] = candidates.as_slice() else {
9096        return None;
9097    };
9098    let original_body = cpp_body_node(*class_node)?;
9099    let name = class_like_name(*class_node, source, ancestry)?;
9100    if name.is_empty() || cpp_export_macro_token(&name) {
9101        return None;
9102    }
9103
9104    let mut sibling = node.next_named_sibling();
9105    let (class_close_start, class_close_end, class_close_line) = loop {
9106        let current = sibling?;
9107        let next = current.next_named_sibling();
9108        if cpp_is_stray_close_brace(current, source)
9109            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
9110        {
9111            let semicolon = next.expect("checked above");
9112            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
9113                return None;
9114            }
9115            break (
9116                current.start_byte(),
9117                semicolon.end_byte(),
9118                semicolon.end_position().row + 1,
9119            );
9120        }
9121        sibling = next;
9122    };
9123    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
9124    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
9125    let root = tree.root_node();
9126    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
9127    // The region reparse is its own tree, so it needs its own parent index;
9128    // the caller's index answers nothing about these nodes.
9129    let reparsed_ancestry = ParentIndex::new(root);
9130    let reparsed =
9131        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
9132    if reparsed.name != name
9133        || reparsed.declaration_node.start_byte() != class_node.start_byte()
9134        || reparsed.body.start_byte() != original_body.start_byte()
9135        || class_close_start <= reparsed.body.end_byte()
9136        || class_close_end <= class_node.end_byte()
9137    {
9138        return None;
9139    }
9140    Some(CppSentinelDirectBodyClassRegion {
9141        namespace_components,
9142        class_start: reparse_start,
9143        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
9144            node.start_position().row + 1
9145        }),
9146        class_close_end,
9147        class_close_line,
9148        name,
9149    })
9150}
9151
9152/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
9153/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
9154///
9155/// The parser puts the namespace opener and the malformed function in one root
9156/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
9157/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
9158/// the malformed function must begin with an all-caps type, then an ERROR whose
9159/// sole identifier is `namespace`, followed by the inner namespace identifier
9160/// and a compound body; and that body must contain a complete named class or a
9161/// structurally fragmented class prefix. A text reparse cannot prove any of
9162/// those ownership boundaries.
9163fn cpp_nested_namespace_sentinel<'tree>(
9164    node: Node<'tree>,
9165    source: &str,
9166    ancestry: &ParentIndex<'tree>,
9167) -> Option<CppNestedNamespaceSentinel<'tree>> {
9168    if !node.has_error() {
9169        return None;
9170    }
9171
9172    let (function, mut namespace_components) = if node.kind() == "ERROR" {
9173        let mut cursor = node.walk();
9174        let functions = node
9175            .named_children(&mut cursor)
9176            .filter(|child| child.kind() == "function_definition")
9177            .collect::<Vec<_>>();
9178        let [function] = functions.as_slice() else {
9179            return None;
9180        };
9181        if !function.has_error() {
9182            return None;
9183        }
9184        let mut cursor = node.walk();
9185        let children = node.children(&mut cursor).collect::<Vec<_>>();
9186        let function_index = children
9187            .iter()
9188            .position(|child| same_node(*child, *function))?;
9189        let [outer_keyword, outer_name, outer_open] =
9190            children.get(function_index.checked_sub(3)?..function_index)?
9191        else {
9192            return None;
9193        };
9194        if outer_keyword.kind() != "namespace"
9195            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
9196            || outer_open.kind() != "{"
9197        {
9198            return None;
9199        }
9200        (
9201            *function,
9202            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
9203        )
9204    } else if node.kind() == "function_definition" {
9205        let declaration_list = node.parent()?;
9206        let namespace = declaration_list.parent()?;
9207        if declaration_list.kind() != "declaration_list"
9208            || namespace.kind() != "namespace_definition"
9209            || namespace.child_by_field_name("body") != Some(declaration_list)
9210        {
9211            return None;
9212        }
9213        (node, Vec::new())
9214    } else {
9215        return None;
9216    };
9217
9218    let mut cursor = function.walk();
9219    let named = function
9220        .named_children(&mut cursor)
9221        .filter(|child| child.kind() != "comment")
9222        .collect::<Vec<_>>();
9223    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
9224        return None;
9225    };
9226    if first_type.kind() != "type_identifier" {
9227        return None;
9228    }
9229    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
9230    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
9231        return None;
9232    }
9233    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
9234        return None;
9235    }
9236    let inner_keyword = inner_error.named_child(0)?;
9237    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
9238        return None;
9239    }
9240    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
9241        return None;
9242    }
9243    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
9244    if inner_name.is_empty() || body.kind() != "compound_statement" {
9245        return None;
9246    }
9247    namespace_components.push(inner_name);
9248
9249    let mut cursor = body.walk();
9250    let has_complete_class = body.named_children(&mut cursor).any(|child| {
9251        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9252            cpp_body_node(class_node).is_some()
9253                && class_like_name(class_node, source, ancestry)
9254                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9255        })
9256    });
9257    if !has_complete_class
9258        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
9259    {
9260        return None;
9261    }
9262
9263    Some(CppNestedNamespaceSentinel {
9264        function,
9265        body: *body,
9266        namespace_components,
9267    })
9268}
9269
9270/// Recognize a namespace-begin sentinel directly beneath the translation unit.
9271///
9272/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
9273/// function whose type is the sentinel, whose declarator is the structured
9274/// qualified name `namespace::a::b`, and whose body contains the namespace
9275/// items. Declaration indexing already reparses this bounded region. The
9276/// inverse scanner retains the original tree, so recover the same namespace
9277/// components from the declarator fields for its lexical-scope metadata.
9278fn cpp_root_namespace_sentinel<'tree>(
9279    node: Node<'tree>,
9280    source: &str,
9281    ancestry: &ParentIndex<'tree>,
9282) -> Option<CppNestedNamespaceSentinel<'tree>> {
9283    if node.kind() != "function_definition"
9284        || !node.has_error()
9285        || node.parent()?.kind() != "translation_unit"
9286    {
9287        return None;
9288    }
9289    let first_type = node.child_by_field_name("type")?;
9290    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
9291    if first_type.kind() != "type_identifier"
9292        || sentinel.is_empty()
9293        || !cpp_export_macro_token(&sentinel)
9294    {
9295        return None;
9296    }
9297    let declarator = node.child_by_field_name("declarator")?;
9298    let body = node.child_by_field_name("body")?;
9299    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
9300        return None;
9301    }
9302    let mut cursor = node.walk();
9303    let named = node
9304        .named_children(&mut cursor)
9305        .filter(|child| child.kind() != "comment")
9306        .collect::<Vec<_>>();
9307    let [named_type, named_declarator, named_body] = named.as_slice() else {
9308        return None;
9309    };
9310    if !same_node(*named_type, first_type)
9311        || !same_node(*named_declarator, declarator)
9312        || !same_node(*named_body, body)
9313    {
9314        return None;
9315    }
9316    let mut declarator_components = Vec::new();
9317    let mut valid_components = true;
9318    walk_named_tree_preorder(declarator, true, |component| {
9319        if !matches!(
9320            component.kind(),
9321            "identifier" | "namespace_identifier" | "type_identifier"
9322        ) {
9323            return WalkControl::Continue;
9324        }
9325        let Some(component) = canonical_cpp_qualified_component(component, source) else {
9326            valid_components = false;
9327            return WalkControl::Break;
9328        };
9329        declarator_components.push(component.name);
9330        WalkControl::SkipChildren
9331    });
9332    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
9333        return None;
9334    }
9335    declarator_components.remove(0);
9336    let namespace_components = declarator_components;
9337    if namespace_components.is_empty()
9338        || namespace_components
9339            .iter()
9340            .any(|component| component.is_empty() || cpp_export_macro_token(component))
9341    {
9342        return None;
9343    }
9344
9345    let mut cursor = body.walk();
9346    let has_complete_class = body.named_children(&mut cursor).any(|child| {
9347        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
9348            cpp_body_node(class_node).is_some()
9349                && class_like_name(class_node, source, ancestry)
9350                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
9351        })
9352    });
9353    if !has_complete_class
9354        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
9355    {
9356        return None;
9357    }
9358
9359    Some(CppNestedNamespaceSentinel {
9360        function: node,
9361        body,
9362        namespace_components,
9363    })
9364}
9365
9366/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
9367/// malformed namespace-sentinel function.  The recovery is deliberately
9368/// structural: the class must be a direct body item, its own class node must be
9369/// erroneous and end before a unique anonymous `}` in the enclosing
9370/// declaration-list, and that namespace's next sibling must be a standalone
9371/// `;`.  The complete interior must pass the existing member-shaped reparse
9372/// gate. This avoids source brace scans and does not borrow a close from an
9373/// unrelated later declaration.
9374fn cpp_sentinel_fragmented_class_tail<'tree>(
9375    function: Node<'tree>,
9376    body: Node<'tree>,
9377    source: &str,
9378    ancestry: &ParentIndex<'tree>,
9379) -> Option<CppSentinelFragmentedClassTail<'tree>> {
9380    let mut cursor = body.walk();
9381    let candidates = body
9382        .named_children(&mut cursor)
9383        .filter_map(|child| {
9384            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
9385                let class_body = cpp_body_node(class_node)?;
9386                if !class_node.has_error() {
9387                    return None;
9388                }
9389                let name = class_like_name(class_node, source, ancestry)?;
9390                let raw_supertypes =
9391                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
9392                        .then(|| extract_cpp_supertypes(class_node, source));
9393                return Some((
9394                    class_node,
9395                    template_node,
9396                    name,
9397                    class_body,
9398                    class_body.start_byte().checked_add(1)?,
9399                    raw_supertypes,
9400                ));
9401            }
9402            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
9403            Some((
9404                child,
9405                None,
9406                prefix.name,
9407                prefix.open,
9408                prefix.open.end_byte(),
9409                prefix.raw_supertypes,
9410            ))
9411        })
9412        .collect::<Vec<_>>();
9413    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
9414        candidates.as_slice()
9415    else {
9416        return None;
9417    };
9418    if name.is_empty() || cpp_export_macro_token(name) {
9419        return None;
9420    }
9421
9422    let (close, semicolon) =
9423        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
9424
9425    let reparse_end = close.start_byte();
9426    if *reparse_start >= reparse_end {
9427        return None;
9428    }
9429    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
9430    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
9431        return None;
9432    }
9433    let class_range = Range {
9434        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9435        end_byte: semicolon.end_byte(),
9436        start_line: template_node.map_or(class_node.start_position().row, |node| {
9437            node.start_position().row
9438        }) + 1,
9439        end_line: semicolon.end_position().row + 1,
9440    };
9441    Some(CppSentinelFragmentedClassTail {
9442        class_node: *class_node,
9443        template_node: *template_node,
9444        name: name.clone(),
9445        raw_supertypes: raw_supertypes.clone(),
9446        fragmented: FragmentedExportBody {
9447            reparse_start: *reparse_start,
9448            reparse_end,
9449            class_range,
9450        },
9451        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
9452    })
9453}
9454
9455/// Recover the class and out-of-line owner scopes from every malformed
9456/// namespace-sentinel region in `root`.
9457///
9458/// This is the shared structural counterpart to
9459/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
9460/// reuses the visitor's sentinel/class admission predicates instead of parsing
9461/// source text a second time.  The returned values own only ranges and names, so
9462/// they can be retained by an inverted usage scan after the tree borrow ends.
9463pub fn cpp_sentinel_recovered_classes(
9464    root: Node<'_>,
9465    source: &str,
9466) -> Vec<CppSentinelRecoveredClass> {
9467    if !root.has_error() {
9468        return Vec::new();
9469    }
9470    // This scan owns its walk of `root`, so it owns the parent index that walk
9471    // asks its ancestor questions through. Built after the error gate: a clean
9472    // tree returns without paying for one.
9473    let ancestry = ParentIndex::new(root);
9474    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
9475    let mut stack = vec![root];
9476    while let Some(current) = stack.pop() {
9477        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
9478            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
9479        {
9480            let namespace_components = cpp_sentinel_recovered_namespace_components(
9481                recovered.function,
9482                &recovered.namespace_components,
9483                source,
9484            );
9485            let fragmented = cpp_sentinel_fragmented_class_tail(
9486                recovered.function,
9487                recovered.body,
9488                source,
9489                &ancestry,
9490            );
9491            let mut class_candidates = Vec::new();
9492            let mut cursor = recovered.body.walk();
9493            for (class_node, template_node) in recovered
9494                .body
9495                .named_children(&mut cursor)
9496                .filter_map(cpp_sentinel_body_class_candidate)
9497            {
9498                let Some(name) = class_like_name(class_node, source, &ancestry) else {
9499                    continue;
9500                };
9501                if name.is_empty() || cpp_export_macro_token(&name) {
9502                    continue;
9503                }
9504                let is_fragmented = fragmented
9505                    .as_ref()
9506                    .is_some_and(|tail| same_node(tail.class_node, class_node));
9507                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
9508                    continue;
9509                }
9510                let class_range = if is_fragmented {
9511                    fragmented
9512                        .as_ref()
9513                        .map(|tail| tail.fragmented.class_range)
9514                        .expect("fragmented class range is present when class matches")
9515                } else {
9516                    cpp_declaration_range(template_node.unwrap_or(class_node))
9517                };
9518                class_candidates.push((class_range, name));
9519            }
9520            if let Some(fragmented) = fragmented
9521                .as_ref()
9522                .filter(|tail| tail.class_node.kind() == "ERROR")
9523            {
9524                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
9525            }
9526
9527            let mut owner_ranges =
9528                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
9529            cpp_sentinel_extend_unique_owner_ranges(
9530                &mut owner_ranges,
9531                cpp_sentinel_recovered_sibling_owner_ranges(
9532                    recovered.function,
9533                    &namespace_components,
9534                    source,
9535                ),
9536            );
9537            for (class_range, name) in class_candidates {
9538                push_cpp_sentinel_recovered_class(
9539                    &mut recovered_classes,
9540                    cpp_declaration_range(recovered.body),
9541                    &namespace_components,
9542                    class_range,
9543                    name,
9544                    &owner_ranges,
9545                );
9546            }
9547
9548            if let Some(declaration_list) = recovered
9549                .function
9550                .parent()
9551                .filter(|parent| parent.kind() == "declaration_list")
9552            {
9553                let outer_namespace =
9554                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
9555                push_cpp_sentinel_sibling_classes(
9556                    &mut recovered_classes,
9557                    declaration_list,
9558                    recovered.function,
9559                    &outer_namespace,
9560                    source,
9561                    &ancestry,
9562                );
9563            }
9564        } else if let Some(region) =
9565            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
9566        {
9567            let namespace_components = cpp_sentinel_recovered_namespace_components(
9568                current,
9569                &region.namespace_components,
9570                source,
9571            );
9572            let owner_container = current
9573                .parent()
9574                .filter(|parent| parent.kind() == "declaration_list")
9575                .unwrap_or(current);
9576            let owner_ranges =
9577                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9578            push_cpp_sentinel_recovered_class(
9579                &mut recovered_classes,
9580                cpp_declaration_range(owner_container),
9581                &namespace_components,
9582                Range {
9583                    start_byte: region.class_start,
9584                    end_byte: region.class_close_end,
9585                    start_line: region.class_start_line,
9586                    end_line: region.class_close_line,
9587                },
9588                region.name,
9589                &owner_ranges,
9590            );
9591        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
9592            // A generic sentinel-prefixed class can be reduced as a malformed
9593            // function/ERROR without the explicit `namespace X` token pair.
9594            // Reuse the declaration visitor's bounded reparse and retain only
9595            // the recovered class identity/range here.
9596            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
9597                region;
9598            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
9599                continue;
9600            };
9601            let root = tree.root_node();
9602            let template_node = cpp_sentinel_reparsed_leading_template(root);
9603            // A region reparse is its own tree and needs its own parent index.
9604            let reparsed_ancestry = ParentIndex::new(root);
9605            let Some(reparsed_class) =
9606                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
9607            else {
9608                continue;
9609            };
9610            let class_node = reparsed_class.declaration_node;
9611            let name = reparsed_class.name;
9612            let namespace_components =
9613                cpp_sentinel_recovered_namespace_components(current, &[], source);
9614            let owner_container = current
9615                .parent()
9616                .filter(|parent| parent.kind() == "declaration_list")
9617                .unwrap_or(current);
9618            let mut owner_ranges =
9619                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
9620            cpp_sentinel_extend_unique_owner_ranges(
9621                &mut owner_ranges,
9622                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
9623            );
9624            push_cpp_sentinel_recovered_class(
9625                &mut recovered_classes,
9626                cpp_declaration_range(owner_container),
9627                &namespace_components,
9628                Range {
9629                    start_byte: class_start,
9630                    end_byte: close_end,
9631                    start_line: class_node.start_position().row + 1,
9632                    end_line: class_node.end_position().row + 1,
9633                },
9634                name,
9635                &owner_ranges,
9636            );
9637            if owner_container.kind() == "declaration_list" {
9638                push_cpp_sentinel_sibling_classes(
9639                    &mut recovered_classes,
9640                    owner_container,
9641                    current,
9642                    &namespace_components,
9643                    source,
9644                    &ancestry,
9645                );
9646            }
9647        }
9648
9649        let mut cursor = current.walk();
9650        stack.extend(current.named_children(&mut cursor));
9651    }
9652    // A shallower sentinel can expose nested classes as apparent namespace
9653    // siblings even after a deeper sentinel proves that a containing class
9654    // owns their ranges. Drop those shadow descriptors; scope recovery starts
9655    // from the proven containing class and appends parser-visible class
9656    // ancestors, preserving the full `Outer::Inner` chain.
9657    let shadowed = recovered_classes
9658        .iter()
9659        .map(|candidate| {
9660            recovered_classes.iter().any(|container| {
9661                container.class_range.start_byte <= candidate.class_range.start_byte
9662                    && container.class_range.end_byte >= candidate.class_range.end_byte
9663                    && container.class_range != candidate.class_range
9664                    && container.namespace_scope_components.len()
9665                        > candidate.namespace_scope_components.len()
9666                    && container
9667                        .namespace_scope_components
9668                        .starts_with(&candidate.namespace_scope_components)
9669            })
9670        })
9671        .collect::<Vec<_>>();
9672    let mut index = 0usize;
9673    recovered_classes.retain(|_| {
9674        let keep = !shadowed[index];
9675        index += 1;
9676        keep
9677    });
9678    recovered_classes
9679}
9680
9681/// A flat sentinel can swallow the first class while leaving later classes and
9682/// their out-of-line definitions as ordinary declaration-list siblings.  Once
9683/// the malformed class proves the sentinel envelope, retain those structurally
9684/// complete sibling classes under the same surviving namespace so every member
9685/// owner in the region uses one recovery contract.
9686fn push_cpp_sentinel_sibling_classes<'tree>(
9687    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9688    declaration_list: Node<'tree>,
9689    sentinel_node: Node<'tree>,
9690    namespace_components: &[String],
9691    source: &str,
9692    ancestry: &ParentIndex<'tree>,
9693) {
9694    let owner_ranges =
9695        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
9696    let namespace_range = cpp_declaration_range(declaration_list);
9697    let mut cursor = declaration_list.walk();
9698    for (class_node, template_node) in declaration_list
9699        .named_children(&mut cursor)
9700        .filter(|child| !same_node(*child, sentinel_node))
9701        .filter_map(cpp_sentinel_body_class_candidate)
9702    {
9703        let Some(name) = class_like_name(class_node, source, ancestry) else {
9704            continue;
9705        };
9706        if name.is_empty()
9707            || cpp_export_macro_token(&name)
9708            || cpp_complete_class_body_close(class_node).is_none()
9709        {
9710            continue;
9711        }
9712        push_cpp_sentinel_recovered_class(
9713            recovered_classes,
9714            namespace_range,
9715            namespace_components,
9716            cpp_declaration_range(template_node.unwrap_or(class_node)),
9717            name,
9718            &owner_ranges,
9719        );
9720    }
9721}
9722
9723fn push_cpp_sentinel_recovered_class(
9724    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
9725    namespace_range: Range,
9726    namespace_components: &[String],
9727    class_range: Range,
9728    name: String,
9729    owner_ranges: &[CppSentinelRecoveredOwner],
9730) {
9731    let mut scope_components = namespace_components.to_vec();
9732    scope_components.push(name);
9733    let owner_ranges = owner_ranges
9734        .iter()
9735        .filter(|owner| owner.scope_components.starts_with(&scope_components))
9736        .cloned()
9737        .collect::<Vec<_>>();
9738    if recovered_classes.iter().any(|existing| {
9739        existing.class_range == class_range && existing.scope_components == scope_components
9740    }) {
9741        return;
9742    }
9743    recovered_classes.push(CppSentinelRecoveredClass {
9744        namespace_range,
9745        namespace_scope_components: namespace_components.to_vec(),
9746        class_range,
9747        scope_components,
9748        owner_ranges,
9749    });
9750}
9751
9752fn cpp_sentinel_recovered_namespace_components(
9753    function: Node<'_>,
9754    recovered_components: &[String],
9755    source: &str,
9756) -> Vec<String> {
9757    let mut ancestor_components = Vec::new();
9758    let mut ancestor = function.parent();
9759    while let Some(current) = ancestor {
9760        if current.kind() == "namespace_definition"
9761            && let Some(name_node) = current.child_by_field_name("name")
9762            && let Some(components) = cpp_name_components(name_node, source)
9763        {
9764            ancestor_components.push(
9765                components
9766                    .into_iter()
9767                    .map(|component| component.name)
9768                    .collect::<Vec<_>>(),
9769            );
9770        }
9771        ancestor = current.parent();
9772    }
9773    ancestor_components.reverse();
9774    let mut ancestors = ancestor_components
9775        .into_iter()
9776        .flatten()
9777        .collect::<Vec<_>>();
9778
9779    let overlap = (0..=ancestors.len().min(recovered_components.len()))
9780        .rev()
9781        .find(|length| {
9782            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
9783        })
9784        .unwrap_or(0);
9785    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
9786    ancestors
9787}
9788
9789fn cpp_sentinel_recovered_owner_ranges(
9790    body: Node<'_>,
9791    namespace_components: &[String],
9792    source: &str,
9793) -> Vec<CppSentinelRecoveredOwner> {
9794    let mut owners = Vec::new();
9795    walk_named_tree_preorder(body, true, |node| {
9796        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9797    });
9798    owners
9799}
9800
9801fn cpp_sentinel_collect_owner_range(
9802    node: Node<'_>,
9803    namespace_components: &[String],
9804    source: &str,
9805    owners: &mut Vec<CppSentinelRecoveredOwner>,
9806) -> WalkControl {
9807    if node.kind() != "function_definition" {
9808        return WalkControl::Continue;
9809    }
9810    let Some(function_declarator) = extract_function_declarator(node) else {
9811        return WalkControl::Continue;
9812    };
9813    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
9814        return WalkControl::Continue;
9815    };
9816    let Some(mut components) = cpp_name_components(name_node, source) else {
9817        return WalkControl::Continue;
9818    };
9819    if components.len() <= 1 {
9820        return WalkControl::Continue;
9821    }
9822    components.pop();
9823    let mut owner_components = components
9824        .into_iter()
9825        .map(|component| component.name)
9826        .collect::<Vec<_>>();
9827    let overlap = (0..=namespace_components.len().min(owner_components.len()))
9828        .rev()
9829        .find(|length| {
9830            owner_components[..*length]
9831                == namespace_components[namespace_components.len().saturating_sub(*length)..]
9832        })
9833        .unwrap_or(0);
9834    let mut scope_components = namespace_components.to_vec();
9835    scope_components.extend(owner_components.drain(overlap..));
9836    if scope_components.len() <= namespace_components.len() {
9837        return WalkControl::Continue;
9838    }
9839    let range = cpp_declaration_range(node);
9840    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
9841        existing.range == range && existing.scope_components == scope_components
9842    }) {
9843        owners.push(CppSentinelRecoveredOwner {
9844            range,
9845            owner_name_start_byte: name_node.start_byte(),
9846            namespace_component_count: namespace_components.len(),
9847            scope_components,
9848        });
9849    }
9850    WalkControl::Continue
9851}
9852
9853fn cpp_sentinel_extend_unique_owner_ranges(
9854    owners: &mut Vec<CppSentinelRecoveredOwner>,
9855    additional: Vec<CppSentinelRecoveredOwner>,
9856) {
9857    for owner in additional {
9858        if !owners.iter().any(|existing| {
9859            existing.range == owner.range && existing.scope_components == owner.scope_components
9860        }) {
9861            owners.push(owner);
9862        }
9863    }
9864}
9865
9866fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
9867    if node.kind() != "ERROR" || node.named_child_count() != 1 {
9868        return false;
9869    }
9870    let Some(end_name) = node.named_child(0) else {
9871        return false;
9872    };
9873    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
9874        return false;
9875    }
9876    let mut cursor = node.walk();
9877    node.children(&mut cursor)
9878        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
9879}
9880
9881/// Collect owner definitions that the malformed sentinel left as later
9882/// declaration-list siblings. Parser-visible namespace siblings are a hard
9883/// boundary: their declarations must keep their own lexical namespace.
9884fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9885    parent: Node<'_>,
9886    sentinel_node: Node<'_>,
9887    namespace_components: &[String],
9888    source: &str,
9889) -> Vec<CppSentinelRecoveredOwner> {
9890    let mut owners = Vec::new();
9891    let mut after_sentinel = false;
9892    let mut cursor = parent.walk();
9893    for child in parent.named_children(&mut cursor) {
9894        if !after_sentinel {
9895            if same_node(child, sentinel_node) {
9896                after_sentinel = true;
9897            }
9898            continue;
9899        }
9900        walk_named_tree_preorder(child, true, |node| {
9901            if node.kind() == "namespace_definition" {
9902                return WalkControl::SkipChildren;
9903            }
9904            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9905        });
9906    }
9907    owners
9908}
9909
9910/// Collect owner definitions after a malformed namespace, stopping only at
9911/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
9912/// enclosing container is not trusted to belong to the recovered namespace.
9913fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9914    parent: Node<'_>,
9915    sentinel_node: Node<'_>,
9916    namespace_components: &[String],
9917    source: &str,
9918) -> Option<Vec<CppSentinelRecoveredOwner>> {
9919    let mut owners = Vec::new();
9920    let mut after_namespace = false;
9921    let mut cursor = parent.walk();
9922    for child in parent.named_children(&mut cursor) {
9923        if !after_namespace {
9924            if same_node(child, sentinel_node) {
9925                after_namespace = true;
9926            }
9927            continue;
9928        }
9929        if cpp_sentinel_namespace_end(child, source) {
9930            return Some(owners);
9931        }
9932        walk_named_tree_preorder(child, true, |node| {
9933            if node.kind() == "namespace_definition" {
9934                return WalkControl::SkipChildren;
9935            }
9936            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
9937        });
9938    }
9939    None
9940}
9941
9942fn cpp_sentinel_recovered_sibling_owner_ranges(
9943    sentinel_node: Node<'_>,
9944    namespace_components: &[String],
9945    source: &str,
9946) -> Vec<CppSentinelRecoveredOwner> {
9947    let Some(declaration_list) = sentinel_node
9948        .parent()
9949        .filter(|parent| parent.kind() == "declaration_list")
9950    else {
9951        return Vec::new();
9952    };
9953    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
9954        declaration_list,
9955        sentinel_node,
9956        namespace_components,
9957        source,
9958    );
9959
9960    let Some(namespace) = declaration_list
9961        .parent()
9962        .filter(|parent| parent.kind() == "namespace_definition")
9963    else {
9964        return owners;
9965    };
9966    let Some(outer_parent) = namespace.parent() else {
9967        return owners;
9968    };
9969    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
9970        outer_parent,
9971        namespace,
9972        namespace_components,
9973        source,
9974    ) {
9975        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
9976    }
9977    owners
9978}
9979
9980fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
9981    let mut current = function_declarator.child_by_field_name("declarator")?;
9982    loop {
9983        if matches!(
9984            current.kind(),
9985            "qualified_identifier"
9986                | "scoped_identifier"
9987                | "scoped_type_identifier"
9988                | "identifier"
9989                | "field_identifier"
9990                | "operator_name"
9991                | "destructor_name"
9992                | "literal_operator_name"
9993        ) {
9994            return Some(current);
9995        }
9996        current = current
9997            .child_by_field_name("declarator")
9998            .or_else(|| current.child_by_field_name("name"))
9999            .or_else(|| last_named_child(current))?;
10000    }
10001}
10002
10003fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
10004    match node.kind() {
10005        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
10006            let mut components = match node.child_by_field_name("scope") {
10007                Some(scope) => cpp_name_components(scope, source)?,
10008                None => Vec::new(),
10009            };
10010            let name = node.child_by_field_name("name")?;
10011            components.push(canonical_cpp_qualified_component(name, source)?);
10012            Some(components)
10013        }
10014        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
10015    }
10016}
10017
10018fn cpp_sentinel_fragment_boundary<'tree>(
10019    function: Node<'tree>,
10020    class_node: Node<'tree>,
10021    class_body: Node<'tree>,
10022    source: &str,
10023) -> Option<(Node<'tree>, Node<'tree>)> {
10024    let declaration_list = function.parent()?;
10025    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
10026        return None;
10027    }
10028    let namespace = declaration_list.parent()?;
10029    if namespace.kind() != "namespace_definition"
10030        || namespace.child_by_field_name("body") != Some(declaration_list)
10031    {
10032        return None;
10033    }
10034    let mut cursor = declaration_list.walk();
10035    let closes = declaration_list
10036        .children(&mut cursor)
10037        .filter(|child| {
10038            !child.is_named()
10039                && child.kind() == "}"
10040                && child.start_byte() >= function.end_byte()
10041                && child.start_byte() > class_node.end_byte()
10042                && child.start_byte() > class_body.start_byte()
10043        })
10044        .collect::<Vec<_>>();
10045    let [close] = closes.as_slice() else {
10046        return None;
10047    };
10048    let semicolon = namespace.next_named_sibling()?;
10049    if !cpp_is_stray_semicolon(semicolon, source)
10050        || close.end_byte() != namespace.end_byte()
10051        || semicolon.start_byte() < namespace.end_byte()
10052    {
10053        return None;
10054    }
10055    Some((*close, semicolon))
10056}
10057
10058/// Detect the bogus declaration/function tree that tree-sitter recovers for a
10059/// region prefixed by an object-like macro sentinel the parser cannot see
10060/// (issue #941), and return the byte range `[start, end)` of the swallowed
10061/// declaration interior to reparse.
10062///
10063/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
10064/// `function_definition` whose first non-comment named child is the sentinel
10065/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
10066/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
10067/// the real items.
10068/// `start` is the end of the sentinel identifier -- everything after it is the
10069/// genuine source. `end` is the node's end, extended across any trailing empty
10070/// `;` statement the mis-parse displaced past the node (the class/struct closing
10071/// semicolon), so the reparse sees a complete, brace-balanced item.
10072///
10073/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
10074/// node (`has_error`). Unknown annotation/export macros can make a real callable
10075/// error-recovered even though tree-sitter still preserves its declarator, so a
10076/// preserved callable is admitted only when a displaced class keyword precedes
10077/// that declarator. The clean-reparse-to-items gate in
10078/// `cpp_reparsed_items_are_indexable` is the final arbiter.
10079/// Return the reparse start and, when present, the structurally recovered class
10080/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
10081/// separately from the reparse start because an opaque template-declaration
10082/// macro may precede it.
10083fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
10084    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
10085    {
10086        return None;
10087    }
10088    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
10089    // retain a valid function declarator despite the unknown export macro making
10090    // the outer node erroneous. Remember that declarator for the ordering gate
10091    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
10092    // class keyword precedes a spurious callable assembled from a later member.
10093    let mut declarator_cursor = node.walk();
10094    let preserved_callable = node
10095        .children_by_field_name("declarator", &mut declarator_cursor)
10096        .find_map(extract_function_declarator);
10097    // Leading documentation comments are attached to the malformed
10098    // `function_definition` as named children.  They are not part of the
10099    // sentinel prefix, so select the first non-comment child structurally
10100    // rather than requiring the sentinel to be child zero.  This is the shape
10101    // emitted for nlohmann/json's `basic_json`: its class documentation comment
10102    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
10103    // envelope otherwise ends at the first nested union.
10104    let mut cursor = node.walk();
10105    let first = node
10106        .named_children(&mut cursor)
10107        .find(|child| child.kind() != "comment")?;
10108    if first.kind() != "type_identifier" {
10109        return None;
10110    }
10111    let sentinel = normalize_cpp_whitespace(node_text(first, source));
10112    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
10113        return None;
10114    }
10115    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
10116    // makes the trailing sentinel of one region and the leading sentinel of the
10117    // next both land as bare macro-token identifiers ahead of the real content.
10118    // Advance past every leading macro-token identifier so the reparse begins at
10119    // genuine source rather than another sentinel that would re-form the bogus
10120    // shape and fail the reparse gate.
10121    let mut start = first.end_byte();
10122    let mut after_first = false;
10123    let mut cursor = node.walk();
10124    for child in node.named_children(&mut cursor) {
10125        if !after_first {
10126            if same_node(child, first) {
10127                after_first = true;
10128            }
10129            continue;
10130        }
10131        if matches!(child.kind(), "identifier" | "type_identifier")
10132            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
10133        {
10134            start = child.end_byte();
10135        } else {
10136            break;
10137        }
10138    }
10139    // An additional opaque template-declaration macro before a class can be
10140    // folded into the bogus function's qualified declarator.  In that shape
10141    // the macro is not a direct sibling we can skip above; tree-sitter exposes
10142    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
10143    // Reparse from that keyword (or a real preceding `template` keyword) so the
10144    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
10145    // a class nested in a genuine sentinel-wrapped namespace lies after the
10146    // body opening and must not change the established region start.
10147    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
10148    let mut class_start = None;
10149    let mut template_start = None;
10150    let mut stack = vec![node];
10151    while let Some(current) = stack.pop() {
10152        if current.start_byte() >= prefix_end {
10153            continue;
10154        }
10155        if matches!(
10156            current.kind(),
10157            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
10158        ) {
10159            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
10160                "class" | "struct" | "union" | "enum" => {
10161                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
10162                        seen.min(current.start_byte())
10163                    }));
10164                }
10165                "template" => {
10166                    template_start =
10167                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
10168                            seen.min(current.start_byte())
10169                        }));
10170                }
10171                _ => {}
10172            }
10173        }
10174        let mut cursor = current.walk();
10175        stack.extend(current.children(&mut cursor));
10176    }
10177    if preserved_callable.is_some_and(|callable| {
10178        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
10179    }) {
10180        return None;
10181    }
10182    if let Some(class_start) = class_start {
10183        start = template_start
10184            .filter(|template_start| *template_start < class_start)
10185            .unwrap_or(class_start);
10186    }
10187    Some((start, class_start))
10188}
10189
10190/// Locate a sentinel-prefixed class whose malformed declaration was split across
10191/// root-level siblings. The true class close is represented structurally as a
10192/// lone `}` error followed by the class's displaced `;`; nested method/body
10193/// errors are not direct siblings of the sentinel node and therefore cannot
10194/// satisfy this pair.
10195fn cpp_sentinel_macro_class_region<'tree>(
10196    node: Node<'tree>,
10197    source: &str,
10198) -> Option<(usize, usize, usize, usize, usize, usize)> {
10199    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
10200        return None;
10201    };
10202    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
10203        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
10204        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
10205    if class_start >= body_open_start {
10206        return None;
10207    }
10208    let sibling_close = {
10209        let mut sibling = node.next_named_sibling();
10210        let mut found = None;
10211        while let Some(current) = sibling {
10212            let next = current.next_named_sibling();
10213            if cpp_is_stray_close_brace(current, source)
10214                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
10215            {
10216                let semicolon = next.expect("checked above");
10217                found = Some((
10218                    current.start_byte(),
10219                    semicolon.end_byte(),
10220                    semicolon.end_position().row + 1,
10221                ));
10222                break;
10223            }
10224            sibling = next;
10225        }
10226        found
10227    };
10228    // A stray `};` sibling is this class's close only when the bounded reparse
10229    // agrees the first body-bearing class ENDS there. When the malformed
10230    // envelope swallowed the class's true close, the scan can promote a much
10231    // later scope's close instead -- in protobuf-generated headers
10232    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
10233    // `struct TableStruct_*` paired with the first message class's `};`, making
10234    // the recovered "class body" span whole `namespace {}` blocks and minting
10235    // namespace-scope classes as nested members of the recovered class, which
10236    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
10237    // (#2275). On disagreement, fall through to the suffix-reparse boundary
10238    // below, which derives the close from the class node's own balanced body
10239    // range.
10240    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
10241        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
10242            return false;
10243        };
10244        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10245        // A region reparse is its own tree and needs its own parent index.
10246        let reparsed_ancestry = ParentIndex::new(tree.root_node());
10247        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
10248            tree.root_node(),
10249            template_node,
10250            source,
10251            &reparsed_ancestry,
10252        ) else {
10253            return false;
10254        };
10255        let body = reparsed_class.body;
10256        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
10257    });
10258    let (class_close_start, class_close_end, class_close_line) =
10259        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
10260            (class_close_start, class_close_end, class_close_line)
10261        } else {
10262            // When the malformed envelope itself is an ERROR, tree-sitter can
10263            // leave the class's balanced close in the source while promoting
10264            // all following members to siblings. Reparse the complete suffix
10265            // and use the first body-bearing class node's own field range as
10266            // the partition boundary. This keeps balancing in tree-sitter and
10267            // preserves the source's original byte offsets.
10268            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
10269            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
10270            // A region reparse is its own tree and needs its own parent index.
10271            let reparsed_ancestry = ParentIndex::new(tree.root_node());
10272            let reparsed_class = cpp_sentinel_reparsed_class(
10273                tree.root_node(),
10274                template_node,
10275                source,
10276                &reparsed_ancestry,
10277            )?;
10278            let body = reparsed_class.body;
10279            let class_close_end = body.end_byte();
10280            let class_close_start = class_close_end.checked_sub(1)?;
10281            let class_close_line = body.end_position().row + 1;
10282            (class_close_start, class_close_end, class_close_line)
10283        };
10284    if class_close_start <= class_start {
10285        return None;
10286    }
10287
10288    // Reparse only far enough to expose the class body opening. This is a
10289    // structured check that the candidate really begins with a body-bearing
10290    // class-like item; the original malformed tree cannot provide that node.
10291    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
10292    let class_root = tree.root_node();
10293    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
10294    // A region reparse is its own tree and needs its own parent index.
10295    let reparsed_ancestry = ParentIndex::new(class_root);
10296    let reparsed_class =
10297        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
10298    let body = reparsed_class.body;
10299    // The class body opening must agree with the malformed wrapper's structured
10300    // body field. This rejects an inner nested class while permitting later
10301    // members to remain fragmented as root-level siblings in the bounded parse.
10302    if body.start_byte() != body_open_start {
10303        return None;
10304    }
10305    let body_start = body.start_byte().checked_add(1)?;
10306    (body_start < class_close_start).then_some((
10307        reparse_start,
10308        class_start,
10309        body_start,
10310        class_close_start,
10311        class_close_end,
10312        class_close_line,
10313    ))
10314}
10315
10316/// Find the `{` token immediately following the class/struct/union/enum token
10317/// at `class_start` in the malformed tree. The token is anonymous in the C++
10318/// grammar, so this deliberately walks all children (not only named children)
10319/// and relies on sibling structure rather than source-text searching.
10320fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
10321    let mut stack = vec![node];
10322    while let Some(current) = stack.pop() {
10323        if current.start_byte() == class_start
10324            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
10325        {
10326            let mut sibling = current.next_sibling();
10327            while let Some(candidate) = sibling {
10328                if candidate.kind() == "{" {
10329                    return Some(candidate.start_byte());
10330                }
10331                sibling = candidate.next_sibling();
10332            }
10333        }
10334        let mut cursor = current.walk();
10335        stack.extend(current.children(&mut cursor));
10336    }
10337    None
10338}
10339
10340/// The class body that tree-sitter displaced out of a sentinel-prefixed
10341/// declaration and left as the malformed node's next sibling.
10342///
10343/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
10344/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
10345/// the last child of that `ERROR` and its `{` opens a sibling
10346/// `compound_statement` instead. The body is still the malformed tree's own
10347/// structured token, which is what the caller's `body.start_byte() !=
10348/// body_open_start` agreement check needs; it just is not reachable by walking
10349/// forward from the class token inside the node.
10350fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
10351    node.next_named_sibling()
10352        .filter(|sibling| sibling.kind() == "compound_statement")
10353}
10354
10355fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
10356    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
10357    let mut end = if class_start.is_some() {
10358        cpp_macro_prefixed_class_end(source, start)?
10359    } else {
10360        node.end_byte()
10361    };
10362    if class_start.is_none()
10363        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
10364    {
10365        end = end.max(namespace_end);
10366    }
10367    let mut sibling = node.next_named_sibling();
10368    while let Some(current) = sibling {
10369        if !cpp_is_stray_semicolon(current, source) {
10370            break;
10371        }
10372        end = current.end_byte();
10373        sibling = current.next_named_sibling();
10374    }
10375    (start < end).then_some((start, end))
10376}
10377
10378/// Extend a sentinel reparse through a following namespace that tree-sitter
10379/// flattened into the sentinel node's sibling list.
10380///
10381/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
10382/// unknown macro becomes a false function return type and consumes the first
10383/// namespace body. A second `namespace detail` then loses its enclosing node:
10384/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
10385/// attaches its declarations to the surrounding error tree. Reparse from that
10386/// structured keyword so tree-sitter, rather than a source-text brace scan,
10387/// supplies the complete namespace boundary.
10388fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
10389    let mut sibling = node.next_sibling();
10390    let keyword = loop {
10391        let candidate = sibling?;
10392        sibling = candidate.next_sibling();
10393        if candidate.kind() != "comment" {
10394            break candidate;
10395        }
10396    };
10397    if keyword.kind() != "namespace" {
10398        return None;
10399    }
10400    let name = loop {
10401        let candidate = sibling?;
10402        sibling = candidate.next_sibling();
10403        if candidate.kind() != "comment" {
10404            break candidate;
10405        }
10406    };
10407    if cpp_namespace_name_components(name, source).is_empty() {
10408        return None;
10409    }
10410    let open = loop {
10411        let candidate = sibling?;
10412        sibling = candidate.next_sibling();
10413        if candidate.kind() != "comment" {
10414            break candidate;
10415        }
10416    };
10417    if open.kind() != "{" {
10418        return None;
10419    }
10420
10421    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
10422    let root = tree.root_node();
10423    let mut cursor = root.walk();
10424    let namespace = root
10425        .named_children(&mut cursor)
10426        .find(|candidate| candidate.kind() != "comment")?;
10427    (namespace.kind() == "namespace_definition"
10428        && namespace.start_byte() == keyword.start_byte()
10429        && namespace.child_by_field_name("body").is_some())
10430    .then_some(namespace.end_byte())
10431}
10432
10433/// Parse the source suffix beginning at a structurally recovered class/template
10434/// keyword and return the end of its first body-bearing class item.  The parser,
10435/// rather than a brace scanner, owns nested-body balancing.  This is needed when
10436/// the original error tree truncates the class and scatters later members as
10437/// top-level siblings.
10438fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
10439    let tree = cpp_reparse_region_items(source, start, source.len())?;
10440    let root = tree.root_node();
10441    let mut cursor = root.walk();
10442    for item in root.named_children(&mut cursor) {
10443        if item.end_byte() <= start || item.kind() == "comment" {
10444            continue;
10445        }
10446        let mut stack = vec![item];
10447        while let Some(current) = stack.pop() {
10448            if matches!(
10449                current.kind(),
10450                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10451            ) && cpp_body_node(current).is_some()
10452            {
10453                return Some(current.end_byte());
10454            }
10455            let mut cursor = current.walk();
10456            stack.extend(current.named_children(&mut cursor));
10457        }
10458        // The recovered prefix is required to begin with the class item.  If
10459        // the first real item is something else, fail closed rather than skip
10460        // arbitrary source looking for a later class.
10461        return None;
10462    }
10463    None
10464}
10465
10466/// An empty `;` statement: the displaced closing semicolon of a struct/class that
10467/// the sentinel mis-parse split off past the bogus function node.
10468fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
10469    node.kind() == "expression_statement"
10470        && node.named_child_count() == 0
10471        && node_text(node, source).trim() == ";"
10472}
10473
10474/// Recover the real field name when a leading object-like annotation macro
10475/// displaces a qualified type into tree-sitter's bit-field recovery shape.
10476///
10477/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
10478/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
10479/// `bitfield_clause` containing an error plus an assignment.  The assignment's
10480/// left field is the only structured declaration name in that malformed tail.
10481/// A real bit-field is excluded by the all-caps macro type and required error.
10482fn recovered_macro_qualified_field_declarators<'tree>(
10483    node: Node<'tree>,
10484    source: &str,
10485) -> Option<Vec<Node<'tree>>> {
10486    if node.kind() != "field_declaration" {
10487        return None;
10488    }
10489    let macro_type = node.child_by_field_name("type")?;
10490    if macro_type.kind() != "type_identifier"
10491        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10492    {
10493        return None;
10494    }
10495    let pseudo_declarator = node.child_by_field_name("declarator")?;
10496    if pseudo_declarator.kind() != "field_identifier" {
10497        return None;
10498    }
10499    let mut cursor = node.walk();
10500    let clause = node
10501        .named_children(&mut cursor)
10502        .find(|child| child.kind() == "bitfield_clause")?;
10503    if !(0..clause.named_child_count()).any(|index| {
10504        clause
10505            .named_child(index)
10506            .is_some_and(|child| child.kind() == "ERROR")
10507    }) {
10508        return None;
10509    }
10510    let mut recovered = Vec::new();
10511    let mut stack = vec![clause];
10512    while let Some(current) = stack.pop() {
10513        if current.kind() == "assignment_expression"
10514            && let Some(left) = current.child_by_field_name("left")
10515            && extract_variable_name(left, source).is_some()
10516        {
10517            recovered.push(left);
10518            break;
10519        }
10520        let mut cursor = current.walk();
10521        stack.extend(current.named_children(&mut cursor));
10522    }
10523    if recovered.is_empty() {
10524        return None;
10525    }
10526    let mut cursor = node.walk();
10527    recovered.extend(
10528        node.children_by_field_name("declarator", &mut cursor)
10529            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
10530    );
10531    Some(recovered)
10532}
10533
10534/// Recover a macro-qualified constructor that tree-sitter represents as one
10535/// field declaration. The constructor call remains inside the direct recovery
10536/// error, while each member initializer becomes a false function declarator.
10537/// The class owner proves the constructor name and lets the caller ignore those
10538/// initializer declarators.
10539fn recovered_macro_qualified_constructor_call<'tree>(
10540    node: Node<'tree>,
10541    class_name: &str,
10542    source: &str,
10543) -> Option<Node<'tree>> {
10544    if node.kind() != "field_declaration" {
10545        return None;
10546    }
10547    let macro_type = node.child_by_field_name("type")?;
10548    if macro_type.kind() != "type_identifier"
10549        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10550    {
10551        return None;
10552    }
10553    let mut cursor = node.walk();
10554    let bitfield = node
10555        .named_children(&mut cursor)
10556        .find(|child| child.kind() == "bitfield_clause")?;
10557    let error = bitfield
10558        .named_child(0)
10559        .filter(|child| child.kind() == "ERROR")?;
10560    let mut stack = vec![error];
10561    while let Some(current) = stack.pop() {
10562        if current.kind() == "call_expression"
10563            && current
10564                .child_by_field_name("function")
10565                .is_some_and(|function| node_text(function, source) == class_name)
10566            && current
10567                .child_by_field_name("arguments")
10568                .is_some_and(|arguments| arguments.kind() == "argument_list")
10569        {
10570            return Some(current);
10571        }
10572        let mut cursor = current.walk();
10573        stack.extend(current.named_children(&mut cursor));
10574    }
10575    None
10576}
10577
10578/// Recover a macro-qualified member function declaration that tree-sitter
10579/// represents as a pseudo-field. An object-like export macro before a qualified
10580/// return type can displace the namespace and type into an ERROR/bitfield
10581/// recovery, leaving the callable as a structured `call_expression`.
10582///
10583/// The caller must route this shape before ordinary declarator classification;
10584/// otherwise the displaced namespace identifier is published as a field.
10585fn recovered_macro_qualified_function_call<'tree>(
10586    node: Node<'tree>,
10587    source: &str,
10588) -> Option<Node<'tree>> {
10589    if node.kind() != "field_declaration" {
10590        return None;
10591    }
10592    let macro_type = node.child_by_field_name("type")?;
10593    if macro_type.kind() != "type_identifier"
10594        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10595    {
10596        return None;
10597    }
10598    let declarator = node.child_by_field_name("declarator")?;
10599    if declarator.kind() != "field_identifier" {
10600        return None;
10601    }
10602    let mut cursor = node.walk();
10603    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10604    if !named.iter().any(|child| {
10605        child.kind() == "storage_class_specifier"
10606            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
10607    }) {
10608        return None;
10609    }
10610    let bitfield = named
10611        .iter()
10612        .find(|child| child.kind() == "bitfield_clause")?;
10613    let mut bitfield_cursor = bitfield.walk();
10614    let payload = bitfield
10615        .named_children(&mut bitfield_cursor)
10616        .collect::<Vec<_>>();
10617    let [displaced_error, call] = payload.as_slice() else {
10618        return None;
10619    };
10620    if displaced_error.kind() != "ERROR"
10621        || displaced_error.named_child_count() != 1
10622        || displaced_error
10623            .named_child(0)
10624            .is_none_or(|child| child.kind() != "identifier")
10625        || call.kind() != "call_expression"
10626        || call
10627            .child_by_field_name("function")
10628            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
10629        || call
10630            .child_by_field_name("arguments")
10631            .is_none_or(|arguments| arguments.kind() != "argument_list")
10632    {
10633        return None;
10634    }
10635    Some(*call)
10636}
10637
10638fn recovered_macro_qualified_function_parameters(
10639    arguments: Node<'_>,
10640    source: &str,
10641) -> Option<(String, Vec<String>)> {
10642    if arguments.kind() != "argument_list" {
10643        return None;
10644    }
10645    let mut cursor = arguments.walk();
10646    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
10647    if named.is_empty() {
10648        return Some(("()".to_string(), Vec::new()));
10649    }
10650    let mut types = Vec::new();
10651    let mut labels = Vec::new();
10652    let mut index = 0;
10653    while index < named.len() {
10654        let parameter_type = named[index];
10655        let parameter_name = named.get(index + 1).copied()?;
10656        if !matches!(
10657            parameter_type.kind(),
10658            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
10659        ) || parameter_name.kind() != "ERROR"
10660            || parameter_name.named_child_count() != 1
10661            || parameter_name
10662                .named_child(0)
10663                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
10664        {
10665            return None;
10666        }
10667        let parameter_name = parameter_name.named_child(0)?;
10668        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
10669        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
10670        index += 2;
10671    }
10672    Some((format!("({})", types.join(", ")), labels))
10673}
10674
10675/// Recognize the phantom field tree-sitter emits for a macro-qualified
10676/// function return type.  For example,
10677/// `static API result_type ThresholdForSmallA() { ... }` can become a
10678/// `field_declaration` (`API` as the type and `result_type` as a field name)
10679/// followed by a clean `function_definition` for `ThresholdForSmallA`.
10680///
10681/// Keep this predicate entirely tied to the CST envelope: the type must be an
10682/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
10683/// the declaration must carry a missing semicolon rather than a real one, and
10684/// the immediate named sibling must expose a function declarator.  A real
10685/// macro-decorated field with an explicit semicolon therefore remains a field.
10686pub fn recovered_macro_return_type_node<'tree>(
10687    node: Node<'tree>,
10688    source: &str,
10689) -> Option<Node<'tree>> {
10690    if node.kind() != "field_declaration" {
10691        return None;
10692    }
10693    let macro_type = node.child_by_field_name("type")?;
10694    if macro_type.kind() != "type_identifier"
10695        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
10696    {
10697        return None;
10698    }
10699    let declarator = node.child_by_field_name("declarator")?;
10700    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
10701        return None;
10702    }
10703    let mut has_missing_semicolon = false;
10704    let mut has_real_semicolon = false;
10705    for index in 0..node.child_count() {
10706        let Some(child) = node.child(index) else {
10707            continue;
10708        };
10709        if child.kind() != ";" {
10710            continue;
10711        }
10712        if child.is_missing() {
10713            has_missing_semicolon = true;
10714        } else {
10715            has_real_semicolon = true;
10716        }
10717    }
10718    if !has_missing_semicolon || has_real_semicolon {
10719        return None;
10720    }
10721    let mut next = node.next_named_sibling();
10722    while next.is_some_and(|sibling| sibling.kind() == "comment") {
10723        next = next.and_then(|sibling| sibling.next_named_sibling());
10724    }
10725    let next = next?;
10726    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
10727        return None;
10728    }
10729    let function_declarator = next.child_by_field_name("declarator")?;
10730    extract_function_declarator(function_declarator).map(|_| declarator)
10731}
10732
10733/// Whether `name` is a type parameter of a template declaration that lexically
10734/// encloses `node`. The malformed macro-return field uses the parameter name as
10735/// its pseudo-declarator; preserving that field is necessary to publish a
10736/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
10737/// ancestors instead of interpreting source text so nested templates and
10738/// parser-recovered regions retain their real lexical scopes.
10739pub(crate) fn cpp_active_template_type_parameter<'tree>(
10740    node: Node<'tree>,
10741    name: &str,
10742    source: &str,
10743    ancestry: &ParentIndex<'tree>,
10744) -> bool {
10745    let mut ancestor = ancestry.parent(node);
10746    while let Some(current) = ancestor {
10747        if current.kind() == "template_declaration"
10748            && let Some(parameters) = current.child_by_field_name("parameters")
10749        {
10750            let mut cursor = parameters.walk();
10751            if parameters.named_children(&mut cursor).any(|parameter| {
10752                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
10753                    && cpp_template_parameter_name(parameter, source)
10754                        .is_some_and(|parameter_name| parameter_name == name)
10755            }) {
10756                return true;
10757            }
10758        }
10759        ancestor = ancestry.parent(current);
10760    }
10761    false
10762}
10763
10764/// Reparse the region `[start, end)` of `source` as C++, confined to the region
10765/// via included ranges so every reparsed node keeps its original byte offset and
10766/// line number. The existing visitors read node text from the original source,
10767/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
10768/// `parse_rust_region_tree` technique.
10769fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
10770    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
10771}
10772
10773fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
10774    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
10775        return None;
10776    }
10777    let semicolon = node.next_sibling()?;
10778    if semicolon.kind() != ";" || semicolon.is_missing() {
10779        return None;
10780    }
10781    let row = node.start_position().row;
10782    let mut start = node.start_byte();
10783    let mut sibling = node.prev_sibling();
10784    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
10785        if previous.kind() == ";" {
10786            break;
10787        }
10788        start = previous.start_byte();
10789        sibling = previous.prev_sibling();
10790    }
10791    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
10792}
10793
10794fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
10795    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
10796        return false;
10797    }
10798    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
10799        return false;
10800    }
10801    let Some(declarator) = (if node.kind() == "function_definition" {
10802        node.child_by_field_name("declarator")
10803            .and_then(extract_function_declarator)
10804    } else {
10805        node.named_child(0)
10806            .filter(|child| child.kind() == "function_declarator")
10807    }) else {
10808        return false;
10809    };
10810    let Some(name) = cpp_function_declarator_name_node(declarator) else {
10811        return false;
10812    };
10813    declarator.start_byte() == node.start_byte()
10814        && name.kind() == "identifier"
10815        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
10816}
10817
10818/// Reparse a fragmented class-body interior while preserving its original byte
10819/// and line offsets. Unlike an included-range translation-unit parse, a padded
10820/// prefix keeps C++ preprocessor directives after an access label in the same
10821/// recovery shape tree-sitter produces for a complete class body.
10822fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
10823    let bytes = source.as_bytes();
10824    let prefix = bytes.get(..start)?;
10825    let interior = bytes.get(start..end)?;
10826    let mut padded = Vec::with_capacity(end);
10827    padded.extend(
10828        prefix
10829            .iter()
10830            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
10831    );
10832    padded.extend_from_slice(interior);
10833    let padded = String::from_utf8(padded).ok()?;
10834    let mut parser = Parser::new();
10835    parser
10836        .set_language(&tree_sitter_cpp::LANGUAGE.into())
10837        .ok()?;
10838    parser.parse(&padded, None)
10839}
10840
10841/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
10842/// reparsed interior is indexed only when every top-level named node is a
10843/// well-formed C++ item (or a comment) and at least one real item is present.
10844/// Expression/statement soup surfaces as a top-level `ERROR` or
10845/// `expression_statement`, neither of which is an item kind, so it is rejected.
10846///
10847/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
10848/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
10849/// swallowed by a preceding dangling sentinel) reparses to a real
10850/// `namespace_definition` whose body still holds a bogus `function_definition`,
10851/// so the subtree legitimately carries an error. Container items are admitted
10852/// even with an internal error; the inner bogus function is recovered recursively
10853/// when `visit_function_definition` walks it. Each recursion strips at least one
10854/// leading sentinel, so the region strictly shrinks and recovery terminates.
10855///
10856/// A top-level `function_definition` is the one place we stay strict: it is
10857/// admitted only when it is clean or is itself a sentinel candidate. A function
10858/// that has an error and is not a sentinel is a real callable with a broken body,
10859/// so we refuse the whole reparse and let the ordinary path handle it (preserving
10860/// its real return type rather than re-deriving an implicit one).
10861fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
10862    let mut cursor = root.walk();
10863    let mut saw_item = false;
10864    for child in root.named_children(&mut cursor) {
10865        match child.kind() {
10866            "comment" => {}
10867            "function_definition" => {
10868                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
10869                    return false;
10870                }
10871                saw_item = true;
10872            }
10873            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
10874            _ => return false,
10875        }
10876    }
10877    saw_item
10878}
10879
10880/// Robustness gate for a reparsed fragmented multiple-base export class body
10881/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
10882/// kinds a class body produces when reparsed at translation-unit scope: the
10883/// access-specifier label preceding the first member surfaces as a
10884/// `labeled_statement` wrapping that member, and members surface as
10885/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
10886/// Statement or expression soup surfaces as other top-level kinds and is rejected,
10887/// so only a genuinely member-shaped body is ever re-owned as members; anything
10888/// ambiguous falls back to indexing the class alone.
10889fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
10890    if node.kind() != "ERROR" {
10891        return false;
10892    }
10893    let mut stack = Vec::new();
10894    let mut saw_function_declarator = false;
10895    let mut cursor = node.walk();
10896    for child in node.named_children(&mut cursor) {
10897        stack.push(child);
10898    }
10899    while let Some(current) = stack.pop() {
10900        match current.kind() {
10901            // Tree-sitter may wrap adjacent copy-control declarations in a
10902            // nested ERROR. Keep descending only through ERROR wrappers; the
10903            // actual declaration payload must be a function_declarator.
10904            "ERROR" => {
10905                let mut cursor = current.walk();
10906                stack.extend(current.named_children(&mut cursor));
10907            }
10908            "function_declarator" => saw_function_declarator = true,
10909            _ => return false,
10910        }
10911    }
10912    saw_function_declarator
10913}
10914
10915fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
10916    if node.kind() != "ERROR" {
10917        return false;
10918    }
10919    let mut cursor = node.walk();
10920    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
10921    let [explicit, constructor_error, destructor] = named.as_slice() else {
10922        return false;
10923    };
10924    let Some(constructor) = constructor_error.named_child(0) else {
10925        return false;
10926    };
10927    let Some(constructor_name) =
10928        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
10929    else {
10930        return false;
10931    };
10932    let Some(destructor_name) =
10933        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
10934    else {
10935        return false;
10936    };
10937    let Some(destroyed_type) = destructor_name.named_child(0) else {
10938        return false;
10939    };
10940    explicit.kind() == "explicit_function_specifier"
10941        && constructor_error.kind() == "ERROR"
10942        && constructor_error.named_child_count() == 1
10943        && constructor.kind() == "function_declarator"
10944        && constructor_name.kind() == "identifier"
10945        && destructor.kind() == "function_declarator"
10946        && destructor_name.kind() == "destructor_name"
10947        && destroyed_type.kind() == "identifier"
10948        && node_text(constructor_name, source) == node_text(destroyed_type, source)
10949}
10950
10951fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
10952    if node.kind() != "compound_statement" {
10953        return false;
10954    }
10955    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
10956        return false;
10957    };
10958    if prefix.kind() == "labeled_statement"
10959        && prefix.named_child(0).is_some_and(|label| {
10960            matches!(
10961                node_text(label, source).trim(),
10962                "public" | "private" | "protected"
10963            )
10964        })
10965    {
10966        return prefix.named_children(&mut prefix.walk()).any(|child| {
10967            child.kind() == "declaration"
10968                && child.has_error()
10969                && child
10970                    .named_children(&mut child.walk())
10971                    .any(cpp_reparsed_member_error_is_indexable)
10972        });
10973    }
10974    // A malformed constructor initializer can be split into a declaration
10975    // followed by its compound body when the class prefix already contains
10976    // realistic members. Keep this admission tied to that exact structured
10977    // declaration/error/body chain rather than accepting arbitrary blocks.
10978    prefix.kind() == "declaration"
10979        && prefix.has_error()
10980        && prefix
10981            .named_children(&mut prefix.walk())
10982            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
10983}
10984
10985fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
10986    if !cpp_reparsed_member_error_is_indexable(node) {
10987        return false;
10988    }
10989    let Some(preproc) = node.next_named_sibling() else {
10990        return false;
10991    };
10992    preproc.kind() == "preproc_if"
10993        && preproc.has_error()
10994        && preproc
10995            .named_children(&mut preproc.walk())
10996            .any(|child| child.kind() == "expression_statement" && child.has_error())
10997        && preproc
10998            .next_named_sibling()
10999            .is_some_and(|body| body.kind() == "compound_statement")
11000}
11001
11002/// Return a function body whose braces and ownership are explicit in the
11003/// reparsed class-member tree. An error below a real function envelope is
11004/// recoverable by the ordinary function visitor; a missing/deferred body is
11005/// not, because accepting it would let statement soup masquerade as a member.
11006fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
11007    if node.kind() != "function_definition" {
11008        return None;
11009    }
11010    let body = node.child_by_field_name("body")?;
11011    if body.kind() != "compound_statement" {
11012        return None;
11013    }
11014    let open = body.child(0)?;
11015    let close = body.child(body.child_count().checked_sub(1)?)?;
11016    if open.kind() != "{"
11017        || open.is_missing()
11018        || close.kind() != "}"
11019        || close.is_missing()
11020        || close.end_byte() != body.end_byte()
11021        || body.end_byte() != node.end_byte()
11022    {
11023        return None;
11024    }
11025    Some(body)
11026}
11027
11028fn cpp_reparsed_member_function_errors_are_in_body(
11029    node: Node<'_>,
11030    body: Node<'_>,
11031    source: &str,
11032) -> bool {
11033    let mut cursor = node.walk();
11034    node.children(&mut cursor).all(|child| {
11035        same_node(child, body)
11036            || cpp_reparsed_member_attribute_error(child, source)
11037            || cpp_reparsed_member_signature_identifier_errors(child)
11038            || (!child.has_error() && !child.is_error() && !child.is_missing())
11039    })
11040}
11041
11042/// A complete callable can still carry parser errors in its signature when a
11043/// project annotation is not part of the C++ grammar (`nonneg int`,
11044/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
11045/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
11046/// leaves inside the already-proven callable envelope; structured statements,
11047/// literals, missing tokens, and other malformed signature payload remain
11048/// rejected.
11049fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
11050    if !node.has_error() && !node.is_error() && !node.is_missing() {
11051        return false;
11052    }
11053    let mut stack = vec![node];
11054    let mut saw_error = false;
11055    while let Some(current) = stack.pop() {
11056        if current.is_missing() {
11057            return false;
11058        }
11059        if current.kind() == "ERROR" {
11060            saw_error = true;
11061            let mut cursor = current.walk();
11062            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
11063            if children
11064                .iter()
11065                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
11066            {
11067                return false;
11068            }
11069            stack.extend(children);
11070            continue;
11071        }
11072        let mut cursor = current.walk();
11073        stack.extend(current.children(&mut cursor));
11074    }
11075    saw_error
11076}
11077
11078fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
11079    node.kind() == "ERROR"
11080        && node.named_child_count() == 1
11081        && node.named_child(0).is_some_and(|attribute| {
11082            attribute.kind() == "identifier"
11083                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
11084        })
11085}
11086
11087/// A C++ attribute placed between a member's declarator and body can make
11088/// tree-sitter expose the callable as
11089/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
11090/// Keep this admission tied to that exact node geometry. In particular, an
11091/// arbitrary ERROR or identifier before a compound statement is not enough.
11092fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
11093    let Some(body) = cpp_reparsed_member_function_body(node) else {
11094        return false;
11095    };
11096    let mut cursor = node.walk();
11097    let named = node
11098        .named_children(&mut cursor)
11099        .filter(|child| child.kind() != "comment")
11100        .collect::<Vec<_>>();
11101    let [type_node, error, attribute, body_node] = named.as_slice() else {
11102        return false;
11103    };
11104    if !same_node(*body_node, body)
11105        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11106        || attribute.kind() != "identifier"
11107        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11108        || error.kind() != "ERROR"
11109        || error.named_child_count() != 1
11110    {
11111        return false;
11112    }
11113    error
11114        .named_child(0)
11115        .is_some_and(cpp_reparsed_attribute_callable_declarator)
11116}
11117
11118fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
11119    cpp_structured_type_path(node, source).is_some()
11120        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
11121}
11122
11123fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11124    let Some(body) = cpp_reparsed_member_function_body(node) else {
11125        return false;
11126    };
11127    let mut cursor = node.walk();
11128    let named = node
11129        .named_children(&mut cursor)
11130        .filter(|child| child.kind() != "comment")
11131        .collect::<Vec<_>>();
11132    let [friend, return_error, declarator, body_node] = named.as_slice() else {
11133        return false;
11134    };
11135    let Some(return_type) = return_error.named_child(0) else {
11136        return false;
11137    };
11138    same_node(*body_node, body)
11139        && friend.kind() == "type_identifier"
11140        && node_text(*friend, source) == "friend"
11141        && return_error.kind() == "ERROR"
11142        && return_error.named_child_count() == 1
11143        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11144        && extract_function_declarator(*declarator)
11145            .and_then(cpp_function_declarator_name_node)
11146            .is_some()
11147}
11148
11149fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11150    let Some(body) = cpp_reparsed_member_function_body(node) else {
11151        return false;
11152    };
11153    let mut cursor = node.walk();
11154    let named = node
11155        .named_children(&mut cursor)
11156        .filter(|child| child.kind() != "comment")
11157        .collect::<Vec<_>>();
11158    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
11159        return false;
11160    };
11161    let Some(return_type) = return_error.named_child(0) else {
11162        return false;
11163    };
11164    same_node(*body_node, body)
11165        && prefix
11166            .iter()
11167            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
11168        && attribute.kind() == "type_identifier"
11169        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11170        && return_error.kind() == "ERROR"
11171        && return_error.named_child_count() == 1
11172        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11173        && extract_function_declarator(*declarator)
11174            .and_then(cpp_function_declarator_name_node)
11175            .is_some()
11176}
11177
11178/// An included-range reparse that begins inside a malformed class can merge an
11179/// access label and following template member. Tree-sitter then emits the label
11180/// as the `template_type` name, the template parameter list as its arguments,
11181/// an ERROR-wrapped return type, the callable declarator, and its complete
11182/// body. Admit only that exact structured displacement.
11183fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11184    let Some(body) = cpp_reparsed_member_function_body(node) else {
11185        return false;
11186    };
11187    let mut cursor = node.walk();
11188    let named = node
11189        .named_children(&mut cursor)
11190        .filter(|child| child.kind() != "comment")
11191        .collect::<Vec<_>>();
11192    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
11193        return false;
11194    };
11195    let Some(template_name) = template_type.child_by_field_name("name") else {
11196        return false;
11197    };
11198    let Some(arguments) = template_type.child_by_field_name("arguments") else {
11199        return false;
11200    };
11201    let Some(return_type) = return_error.named_child(0) else {
11202        return false;
11203    };
11204    let mut cursor = template_type.walk();
11205    let template_errors = template_type
11206        .named_children(&mut cursor)
11207        .filter(|child| child.kind() == "ERROR")
11208        .collect::<Vec<_>>();
11209    let [comment_error] = template_errors.as_slice() else {
11210        return false;
11211    };
11212    let mut cursor = comment_error.walk();
11213    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
11214    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
11215        return false;
11216    };
11217    same_node(*body_node, body)
11218        && template_type.kind() == "template_type"
11219        && template_name.kind() == "type_identifier"
11220        && matches!(
11221            node_text(template_name, source).trim(),
11222            "public" | "private" | "protected"
11223        )
11224        && arguments.kind() == "template_argument_list"
11225        && arguments.named_child_count() > 0
11226        && !arguments.has_error()
11227        && !colon.is_named()
11228        && colon.kind() == ":"
11229        && comments.iter().all(|child| child.kind() == "comment")
11230        && !template_keyword.is_named()
11231        && template_keyword.kind() == "template"
11232        && return_error.kind() == "ERROR"
11233        && return_error.named_child_count() == 1
11234        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
11235        && extract_function_declarator(*declarator)
11236            .and_then(cpp_function_declarator_name_node)
11237            .is_some()
11238}
11239
11240/// Return the constructor declaration tree-sitter can merge into an access
11241/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
11242/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
11243/// declaration's apparent type; the callable name must still exactly match the
11244/// recovered class, so unrelated labeled statements are never re-owned.
11245fn cpp_reparsed_preprocessor_constructor<'tree>(
11246    node: Node<'tree>,
11247    class_name: &str,
11248    source: &str,
11249) -> Option<Node<'tree>> {
11250    if node.kind() != "labeled_statement" {
11251        return None;
11252    }
11253    let mut cursor = node.walk();
11254    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11255    let [label, directive_error, declaration] = named.as_slice() else {
11256        return None;
11257    };
11258    if label.kind() != "statement_identifier"
11259        || !matches!(
11260            node_text(*label, source),
11261            "public" | "private" | "protected"
11262        )
11263        || directive_error.kind() != "ERROR"
11264        || directive_error.child_count() != 1
11265        || directive_error
11266            .child(0)
11267            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
11268        || declaration.kind() != "declaration"
11269        || declaration.named_child_count() != 2
11270    {
11271        return None;
11272    }
11273    let apparent_type = declaration.child_by_field_name("type")?;
11274    if apparent_type.kind() != "type_identifier"
11275        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
11276    {
11277        return None;
11278    }
11279    let declarator = declaration.child_by_field_name("declarator")?;
11280    let function = extract_function_declarator(declarator)?;
11281    let name = cpp_function_declarator_name_node(function)?;
11282    (node_text(name, source) == class_name).then_some(*declaration)
11283}
11284
11285fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
11286    if extract_function_declarator(node)
11287        .and_then(cpp_function_declarator_name_node)
11288        .is_some()
11289    {
11290        return true;
11291    }
11292    node.kind() == "init_declarator"
11293        && node
11294            .child_by_field_name("declarator")
11295            .is_some_and(|declarator| declarator.kind() == "identifier")
11296        && node
11297            .child_by_field_name("value")
11298            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
11299}
11300
11301/// Return true for the constrained/attribute form that tree-sitter splits into
11302/// an ERROR declaration, a preprocessor `requires` clause, and a following
11303/// compound statement. The three nodes must remain immediate named siblings;
11304/// this deliberately does not search source text or skip unrelated statements.
11305fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
11306    if node.kind() != "ERROR" || node.named_child_count() != 3 {
11307        return false;
11308    }
11309    let mut cursor = node.walk();
11310    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11311    let [type_node, function_declarator, attribute] = named.as_slice() else {
11312        return false;
11313    };
11314    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
11315        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
11316        || attribute.kind() != "identifier"
11317        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
11318    {
11319        return false;
11320    }
11321    let Some(preproc) =
11322        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11323    else {
11324        return false;
11325    };
11326    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
11327        .filter(|sibling| sibling.kind() == "compound_statement")
11328    else {
11329        return false;
11330    };
11331    let Some(open) = body.child(0) else {
11332        return false;
11333    };
11334    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
11335        return false;
11336    };
11337    let Some(condition) = preproc.child_by_field_name("condition") else {
11338        return false;
11339    };
11340    let mut cursor = preproc.walk();
11341    let payload = preproc
11342        .named_children(&mut cursor)
11343        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
11344        .collect::<Vec<_>>();
11345    let [requires_statement] = payload.as_slice() else {
11346        return false;
11347    };
11348    let requires_clause = requires_statement.named_child(0);
11349
11350    open.kind() == "{"
11351        && !open.is_missing()
11352        && close.kind() == "}"
11353        && !close.is_missing()
11354        && close.end_byte() == body.end_byte()
11355        && requires_statement.kind() == "expression_statement"
11356        && requires_statement.named_child_count() == 1
11357        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
11358}
11359
11360fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
11361    let mut sibling = node.next_named_sibling();
11362    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
11363        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
11364    }
11365    sibling
11366}
11367
11368fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
11369    let mut sibling = node.prev_named_sibling();
11370    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
11371        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
11372    }
11373    sibling
11374}
11375
11376fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
11377    let Some(preproc) =
11378        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
11379    else {
11380        return false;
11381    };
11382    let Some(error) =
11383        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
11384    else {
11385        return false;
11386    };
11387    cpp_reparsed_attribute_requires_error(error, source)
11388}
11389
11390fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
11391    node: Node<'tree>,
11392    source: &str,
11393) -> Option<Node<'tree>> {
11394    if node.kind() != "ERROR" {
11395        return None;
11396    }
11397    let mut cursor = node.walk();
11398    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11399    let [parameter, macro_name, message] = named.as_slice() else {
11400        return None;
11401    };
11402    let parameter_name = parameter.named_child(0)?;
11403    (parameter.kind() == "type_parameter_declaration"
11404        && parameter_name.kind() == "type_identifier"
11405        && macro_name.kind() == "type_identifier"
11406        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11407        && message.kind() == "string_literal")
11408        .then_some(parameter_name)
11409}
11410
11411/// Recognize the alternate constraint-macro prefix where tree-sitter retains
11412/// the complete qualified constraint as a fourth child instead of moving it
11413/// into the following function. Keep the gate tied to a two-type template
11414/// constraint that names the declared type parameter.
11415fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
11416    node: Node<'tree>,
11417    source: &str,
11418) -> Option<Node<'tree>> {
11419    if node.kind() != "ERROR" {
11420        return None;
11421    }
11422    let mut cursor = node.walk();
11423    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
11424    let [parameter, macro_name, message, constraint] = named.as_slice() else {
11425        return None;
11426    };
11427    let parameter_name = parameter.named_child(0)?;
11428    let constraint_scope = constraint.child_by_field_name("scope")?;
11429    let constraint_template = constraint.child_by_field_name("name")?;
11430    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11431    let mut argument_cursor = constraint_arguments.walk();
11432    let constraint_types = constraint_arguments
11433        .named_children(&mut argument_cursor)
11434        .collect::<Vec<_>>();
11435    if parameter.kind() != "type_parameter_declaration"
11436        || parameter_name.kind() != "type_identifier"
11437        || macro_name.kind() != "type_identifier"
11438        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
11439        || message.kind() != "string_literal"
11440        || constraint.kind() != "qualified_identifier"
11441        || constraint_scope.kind() != "namespace_identifier"
11442        || !matches!(
11443            constraint_template.kind(),
11444            "template_function" | "template_type"
11445        )
11446        || !matches!(constraint_types.as_slice(), [left, right]
11447            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11448        || constraint_arguments.has_error()
11449    {
11450        return None;
11451    }
11452    let parameter_text = node_text(parameter_name, source);
11453    let mut stack = constraint_types;
11454    while let Some(current) = stack.pop() {
11455        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
11456            return Some(parameter_name);
11457        }
11458        let mut cursor = current.walk();
11459        stack.extend(current.named_children(&mut cursor));
11460    }
11461    None
11462}
11463
11464fn cpp_reparsed_template_macro_companion_is_indexable(
11465    node: Node<'_>,
11466    parameter_name: Node<'_>,
11467    source: &str,
11468) -> bool {
11469    let Some(body) = cpp_reparsed_member_function_body(node) else {
11470        return false;
11471    };
11472    let mut cursor = node.walk();
11473    let named = node
11474        .named_children(&mut cursor)
11475        .filter(|child| child.kind() != "comment")
11476        .collect::<Vec<_>>();
11477    let [
11478        constraint,
11479        close_error,
11480        storage,
11481        return_error,
11482        declarator,
11483        body_node,
11484    ] = named.as_slice()
11485    else {
11486        return false;
11487    };
11488    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
11489        return false;
11490    };
11491    let Some(constraint_template) = constraint.child_by_field_name("name") else {
11492        return false;
11493    };
11494    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
11495        return false;
11496    };
11497    let Some(return_type) = return_error.named_child(0) else {
11498        return false;
11499    };
11500    let mut cursor = constraint_arguments.walk();
11501    let constraint_types = constraint_arguments
11502        .named_children(&mut cursor)
11503        .collect::<Vec<_>>();
11504    same_node(*body_node, body)
11505        && constraint.kind() == "qualified_identifier"
11506        && constraint_scope.kind() == "namespace_identifier"
11507        && constraint_template.kind() == "template_type"
11508        && matches!(constraint_types.as_slice(), [left, right]
11509            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11510        && !constraint_arguments.has_error()
11511        && close_error.kind() == "ERROR"
11512        && close_error.named_child_count() == 0
11513        && storage.kind() == "storage_class_specifier"
11514        && return_error.kind() == "ERROR"
11515        && return_error.named_child_count() == 1
11516        && return_type.kind() == "identifier"
11517        && node_text(return_type, source) == node_text(parameter_name, source)
11518        && extract_function_declarator(*declarator)
11519            .and_then(cpp_function_declarator_name_node)
11520            .is_some()
11521}
11522
11523fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
11524    node: Node<'tree>,
11525    parameter_name: Node<'_>,
11526    source: &str,
11527) -> Option<Node<'tree>> {
11528    let body = cpp_reparsed_member_function_body(node)?;
11529    let constraint = node.child_by_field_name("type")?;
11530    let constraint_template = constraint.child_by_field_name("name")?;
11531    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
11532    let mut argument_cursor = constraint_arguments.walk();
11533    let constraint_types = constraint_arguments
11534        .named_children(&mut argument_cursor)
11535        .collect::<Vec<_>>();
11536    if constraint.kind() != "qualified_identifier"
11537        || constraint_template.kind() != "template_type"
11538        || !matches!(constraint_types.as_slice(), [left, right]
11539            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
11540        || constraint_arguments.has_error()
11541        || node
11542            .child_by_field_name("body")
11543            .is_none_or(|candidate| !same_node(candidate, body))
11544    {
11545        return None;
11546    }
11547
11548    let mut cursor = node.walk();
11549    let recovery_errors = node
11550        .named_children(&mut cursor)
11551        .filter(|child| child.kind() == "ERROR")
11552        .collect::<Vec<_>>();
11553    if !recovery_errors
11554        .iter()
11555        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
11556        || !recovery_errors.iter().all(|error| {
11557            error.named_child_count() == 0
11558                || cpp_reparsed_constraint_macro_error(*error, source)
11559                || (error.named_child_count() == 1
11560                    && error
11561                        .named_child(0)
11562                        .is_some_and(|child| child.kind() == "function_declarator"))
11563        })
11564    {
11565        return None;
11566    }
11567
11568    let parameter_text = node_text(parameter_name, source);
11569    let mut declarators = node
11570        .child_by_field_name("declarator")
11571        .and_then(extract_function_declarator)
11572        .into_iter()
11573        .collect::<Vec<_>>();
11574    for error in recovery_errors {
11575        let mut stack = vec![error];
11576        while let Some(current) = stack.pop() {
11577            if current.kind() == "function_declarator" {
11578                declarators.push(current);
11579            }
11580            let mut cursor = current.walk();
11581            stack.extend(current.named_children(&mut cursor));
11582        }
11583    }
11584    declarators.into_iter().find(|declarator| {
11585        cpp_function_declarator_name_node(*declarator)
11586            .is_some_and(|name| name.kind() == "identifier")
11587            && declarator
11588                .child_by_field_name("parameters")
11589                .is_some_and(|parameters| {
11590                    parameters
11591                        .named_children(&mut parameters.walk())
11592                        .filter_map(|parameter| parameter.child_by_field_name("type"))
11593                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
11594                })
11595    })
11596}
11597
11598fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
11599    node: Node<'_>,
11600    parameter_name: Node<'_>,
11601    source: &str,
11602) -> bool {
11603    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
11604}
11605
11606fn cpp_reparsed_template_macro_function_companion_is_indexable(
11607    node: Node<'_>,
11608    parameter_name: Node<'_>,
11609    source: &str,
11610) -> bool {
11611    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
11612        return false;
11613    }
11614    let Some(return_type) = node.child_by_field_name("type") else {
11615        return false;
11616    };
11617    let Some(function_declarator) = node
11618        .child_by_field_name("declarator")
11619        .and_then(extract_function_declarator)
11620    else {
11621        return false;
11622    };
11623    if cpp_function_declarator_name_node(function_declarator).is_none()
11624        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
11625    {
11626        return false;
11627    }
11628    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
11629        return false;
11630    };
11631    let parameter_text = node_text(parameter_name, source);
11632    parameters
11633        .named_children(&mut parameters.walk())
11634        .any(|parameter| {
11635            parameter
11636                .child_by_field_name("type")
11637                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
11638        })
11639}
11640
11641fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
11642    if node.kind() != "ERROR" {
11643        return false;
11644    }
11645    let mut stack = vec![node];
11646    while let Some(current) = stack.pop() {
11647        let macro_shape = match current.kind() {
11648            "call_expression" => current
11649                .child_by_field_name("function")
11650                .zip(current.child_by_field_name("arguments")),
11651            "init_declarator" => current
11652                .child_by_field_name("declarator")
11653                .zip(current.child_by_field_name("value")),
11654            _ => None,
11655        };
11656        if let Some((name, arguments)) = macro_shape
11657            && name.kind() == "identifier"
11658            && arguments.kind() == "argument_list"
11659            && arguments.named_child_count() >= 2
11660            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
11661        {
11662            return true;
11663        }
11664        let mut cursor = current.walk();
11665        stack.extend(current.named_children(&mut cursor));
11666    }
11667    false
11668}
11669
11670fn cpp_recovered_template_macro_constructor<'tree>(
11671    node: Node<'tree>,
11672    source: &str,
11673) -> Option<(Node<'tree>, Node<'tree>)> {
11674    let mut prefix = node.prev_named_sibling()?;
11675    while prefix.kind() == "comment" {
11676        prefix = prefix.prev_named_sibling()?;
11677    }
11678    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
11679    let parameter = parameter_name
11680        .parent()
11681        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
11682    let declarator =
11683        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
11684    Some((declarator, parameter))
11685}
11686
11687fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
11688    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
11689        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11690            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
11691                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
11692                    function,
11693                    parameter_name,
11694                    source,
11695                )
11696        });
11697    }
11698    let Some(parameter_name) =
11699        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
11700    else {
11701        return false;
11702    };
11703    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
11704        cpp_reparsed_template_macro_function_companion_is_indexable(
11705            function,
11706            parameter_name,
11707            source,
11708        )
11709    })
11710}
11711
11712fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
11713    let function_name = node
11714        .child_by_field_name("declarator")
11715        .and_then(extract_function_declarator)
11716        .and_then(cpp_function_declarator_name_node);
11717    if let Some(body) = cpp_reparsed_member_function_body(node)
11718        && function_name.is_some()
11719        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
11720    {
11721        return true;
11722    }
11723    cpp_reparsed_attribute_member_function(node, source)
11724        || cpp_reparsed_friend_function_is_indexable(node, source)
11725        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
11726        || cpp_reparsed_access_template_function_is_indexable(node, source)
11727        || cpp_recovered_template_macro_constructor(node, source).is_some()
11728}
11729
11730/// Recognize the three top-level nodes produced when an unknown attribute
11731/// macro separates an inline member's declarator from its body in a reparsed
11732/// class interior: an errorful declaration with a missing semicolon, the macro
11733/// call expression, and the complete compound body. Their adjacency and exact
11734/// structured shapes prove one recoverable member envelope; arbitrary calls or
11735/// blocks do not pass this gate.
11736fn cpp_reparsed_macro_attribute_member_sequence(
11737    children: &[Node<'_>],
11738    index: usize,
11739    source: &str,
11740) -> bool {
11741    let Some(prefix) = children.get(index).copied() else {
11742        return false;
11743    };
11744    let declaration = if prefix.kind() == "labeled_statement" {
11745        prefix
11746            .named_child(prefix.named_child_count().saturating_sub(1))
11747            .filter(|child| child.kind() == "declaration")
11748    } else {
11749        (prefix.kind() == "declaration").then_some(prefix)
11750    };
11751    let Some(declaration) = declaration else {
11752        return false;
11753    };
11754    if !declaration.has_error()
11755        || declaration
11756            .child_by_field_name("declarator")
11757            .and_then(extract_function_declarator)
11758            .and_then(cpp_function_declarator_name_node)
11759            .is_none()
11760    {
11761        return false;
11762    }
11763    let Some(attribute_statement) = children.get(index + 1).copied() else {
11764        return false;
11765    };
11766    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
11767        .then(|| attribute_statement.named_child(0))
11768        .flatten()
11769        .filter(|child| child.kind() == "call_expression")
11770    else {
11771        return false;
11772    };
11773    let Some(attribute_name) = attribute_call
11774        .child_by_field_name("function")
11775        .filter(|function| function.kind() == "identifier")
11776        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
11777    else {
11778        return false;
11779    };
11780    if !cpp_export_macro_token(&attribute_name) {
11781        return false;
11782    }
11783    let Some(body) = children.get(index + 2).copied() else {
11784        return false;
11785    };
11786    body.kind() == "compound_statement"
11787        && body.child(0).is_some_and(|open| open.kind() == "{")
11788        && body
11789            .child(body.child_count().saturating_sub(1))
11790            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
11791        && declaration.end_byte() <= attribute_statement.start_byte()
11792        && attribute_statement.end_byte() <= body.start_byte()
11793}
11794
11795fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
11796    let mut cursor = root.walk();
11797    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
11798    let mut saw_member = false;
11799    let mut index = 0;
11800    while index < children.len() {
11801        let child = children[index];
11802        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
11803            saw_member = true;
11804            index += 3;
11805            continue;
11806        }
11807        if let Some((_, _, fragmented)) = fragmented_plain_class_body(child, source) {
11808            let Some(tree) = cpp_reparse_fragmented_class_body(
11809                source,
11810                fragmented.reparse_start,
11811                fragmented.reparse_end,
11812            ) else {
11813                return false;
11814            };
11815            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
11816                return false;
11817            }
11818            saw_member = true;
11819            index += 1;
11820            while index < children.len()
11821                && children[index].end_byte() <= fragmented.class_range.end_byte
11822            {
11823                index += 1;
11824            }
11825            continue;
11826        }
11827        match child.kind() {
11828            "comment" => {}
11829            "labeled_statement" => saw_member = true,
11830            "function_definition" => {
11831                if child.has_error()
11832                    && !cpp_reparsed_member_function_is_indexable(child, source)
11833                    && cpp_sentinel_macro_region(child, source).is_none()
11834                {
11835                    return false;
11836                }
11837                saw_member = true;
11838            }
11839            "ERROR"
11840                if (cpp_reparsed_member_error_is_indexable(child)
11841                    || cpp_reparsed_adjacent_copy_control_error(child, source))
11842                    && (child
11843                        .next_named_sibling()
11844                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
11845                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
11846            {
11847                saw_member = true;
11848            }
11849            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
11850                saw_member = true;
11851            }
11852            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
11853                saw_member = true;
11854            }
11855            "expression_statement"
11856                if cpp_is_stray_semicolon(child, source)
11857                    && child.prev_named_sibling().is_some_and(|error| {
11858                        cpp_reparsed_member_error_is_indexable(error)
11859                            || cpp_reparsed_adjacent_copy_control_error(error, source)
11860                    }) =>
11861            {
11862                saw_member = true;
11863            }
11864            "compound_statement"
11865                if cpp_reparsed_constructor_body_is_indexable(child, source)
11866                    || cpp_reparsed_attribute_requires_body(child, source) =>
11867            {
11868                saw_member = true;
11869            }
11870            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
11871            _ => return false,
11872        }
11873        index += 1;
11874    }
11875    saw_member
11876}
11877
11878/// Detect the malformed constructor shape that tree-sitter exposes as an
11879/// access-label statement followed by initializer-looking declarations. The
11880/// declarations are not class members: visiting their `location(loc)` and
11881/// `string(s)` function declarators would publish synthetic functions. The
11882/// export-class fallback keeps the original sibling nodes and therefore avoids
11883/// this parser artifact. The returned range identifies the real constructor
11884/// header, which can be reparsed independently as a structured declarator.
11885fn cpp_reparsed_synthetic_initializer_constructor_range(
11886    root: Node<'_>,
11887    class_name: &str,
11888    source: &str,
11889    constructor_end: usize,
11890) -> Option<std::ops::Range<usize>> {
11891    let mut stack = {
11892        let mut cursor = root.walk();
11893        root.named_children(&mut cursor).collect::<Vec<_>>()
11894    };
11895    while let Some(current) = stack.pop() {
11896        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
11897            current,
11898            class_name,
11899            source,
11900            constructor_end,
11901        ) {
11902            return Some(range);
11903        }
11904        if current.kind() == "ERROR" {
11905            let mut cursor = current.walk();
11906            stack.extend(current.named_children(&mut cursor));
11907        }
11908    }
11909    None
11910}
11911
11912fn cpp_reparsed_synthetic_initializer_constructor(
11913    node: Node<'_>,
11914    class_name: &str,
11915    source: &str,
11916    constructor_end: usize,
11917) -> Option<std::ops::Range<usize>> {
11918    if node.kind() != "labeled_statement" {
11919        return None;
11920    }
11921    let mut cursor = node.walk();
11922    let named = node
11923        .named_children(&mut cursor)
11924        .filter(|child| child.kind() != "comment")
11925        .collect::<Vec<_>>();
11926    let label = named.first()?;
11927    if label.kind() != "statement_identifier"
11928        || !matches!(
11929            node_text(*label, source).trim(),
11930            "public" | "private" | "protected"
11931        )
11932    {
11933        return None;
11934    }
11935    let call_error_index = named.iter().position(|child| {
11936        if child.kind() != "ERROR" {
11937            return false;
11938        }
11939        let mut stack = vec![*child];
11940        while let Some(current) = stack.pop() {
11941            if current.kind() == "call_expression"
11942                && current
11943                    .child_by_field_name("function")
11944                    .is_some_and(|function| {
11945                        function.kind() == "identifier"
11946                            && node_text(function, source).trim() == class_name
11947                    })
11948            {
11949                return true;
11950            }
11951            let mut cursor = current.walk();
11952            stack.extend(current.named_children(&mut cursor));
11953        }
11954        false
11955    })?;
11956    let constructor_call = {
11957        let mut stack = vec![named[call_error_index]];
11958        let mut found = None;
11959        while let Some(current) = stack.pop() {
11960            if current.kind() == "call_expression"
11961                && current
11962                    .child_by_field_name("function")
11963                    .is_some_and(|function| {
11964                        function.kind() == "identifier"
11965                            && node_text(function, source).trim() == class_name
11966                    })
11967            {
11968                found = Some(current);
11969                break;
11970            }
11971            let mut cursor = current.walk();
11972            stack.extend(current.named_children(&mut cursor));
11973        }
11974        found
11975    };
11976    let constructor_call = constructor_call?;
11977    named.iter().skip(call_error_index + 1).find(|child| {
11978        child.kind() == "declaration" && child.has_error() && {
11979            let mut cursor = child.walk();
11980            child.named_children(&mut cursor).any(|declarator| {
11981                declarator.kind() == "init_declarator"
11982                    && declarator
11983                        .child_by_field_name("declarator")
11984                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
11985                    && declarator
11986                        .child_by_field_name("value")
11987                        .is_some_and(|value| value.kind() == "initializer_list")
11988            })
11989        }
11990    })?;
11991    Some(constructor_call.start_byte()..constructor_end)
11992}
11993
11994fn cpp_reparsed_exact_constructor_declarator<'tree>(
11995    root: Node<'tree>,
11996    start: usize,
11997    class_name: &str,
11998    source: &str,
11999) -> Option<Node<'tree>> {
12000    let mut candidate = None;
12001    let mut stack = vec![root];
12002    while let Some(current) = stack.pop() {
12003        if current.kind() == "function_declarator"
12004            && current.start_byte() == start
12005            && cpp_function_declarator_name_node(current)
12006                .is_some_and(|name| node_text(name, source).trim() == class_name)
12007        {
12008            if candidate.is_some() {
12009                return None;
12010            }
12011            candidate = Some(current);
12012            continue;
12013        }
12014        let mut cursor = current.walk();
12015        stack.extend(current.named_children(&mut cursor));
12016    }
12017    candidate
12018}
12019
12020fn cpp_is_indexable_item_kind(kind: &str) -> bool {
12021    matches!(
12022        kind,
12023        "namespace_definition"
12024            | "class_specifier"
12025            | "struct_specifier"
12026            | "union_specifier"
12027            | "enum_specifier"
12028            | "function_definition"
12029            | "template_declaration"
12030            | "declaration"
12031            | "field_declaration"
12032            | "alias_declaration"
12033            | "static_assert_declaration"
12034            | "type_definition"
12035            | "using_declaration"
12036            | "linkage_specification"
12037            | "preproc_def"
12038            | "preproc_function_def"
12039            | "preproc_include"
12040            | "preproc_if"
12041            | "preproc_ifdef"
12042            | "preproc_call"
12043    )
12044}
12045
12046#[cfg(test)]
12047mod tests {
12048    use super::*;
12049    use crate::adapter::parse_cpp_file;
12050    use brokk_bifrost_core::analyzer::parsed_file::{
12051        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
12052        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
12053    };
12054    use std::fmt::Write;
12055
12056    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
12057        let mut parser = tree_sitter::Parser::new();
12058        parser
12059            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12060            .unwrap();
12061        let tree = parser.parse(source, None).unwrap();
12062        let file = ProjectFile::new(std::env::temp_dir(), name);
12063        parse_cpp_file(&file, source, &tree)
12064    }
12065
12066    #[test]
12067    fn identifies_export_macro_class_base_displaced_into_declarator() {
12068        let source = r#"#define PROJECT_API_
12069namespace project {
12070namespace internal {
12071template <typename T>
12072class Base {};
12073}
12074template <typename T>
12075class Wrapper;
12076template <>
12077class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
12078}
12079"#;
12080        let mut parser = tree_sitter::Parser::new();
12081        parser
12082            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12083            .unwrap();
12084        let tree = parser.parse(source, None).unwrap();
12085        let start = source.find("internal::Base<int>").expect("base");
12086        let mut base = tree
12087            .root_node()
12088            .descendant_for_byte_range(start, start + 8)
12089            .expect("base syntax");
12090        while base.kind() != "qualified_identifier" {
12091            base = base.parent().expect("qualified base ancestor");
12092        }
12093        assert!(
12094            is_recovered_exported_class_base_type_node(base, source),
12095            "{}",
12096            tree.root_node().to_sexp()
12097        );
12098    }
12099
12100    #[test]
12101    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
12102        let source = r#"namespace control {
12103template <typename T>
12104class AnySpan;
12105template <typename T>
12106class ABSL_ATTRIBUTE_VIEW AnySpan {
12107 public:
12108  int begin() const;
12109};
12110}
12111
12112namespace absl {
12113ABSL_NAMESPACE_BEGIN
12114template <typename T>
12115class ABSL_ATTRIBUTE_VIEW Span {
12116 public:
12117  int begin() const;
12118  int back() const;
12119};
12120
12121int begin();
12122int back();
12123}
12124"#;
12125        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
12126        let declarations = parsed.declarations();
12127        assert!(
12128            declarations
12129                .iter()
12130                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
12131        );
12132        for method in ["begin", "back"] {
12133            assert!(declarations.iter().any(|unit| {
12134                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
12135            }));
12136            assert!(
12137                declarations.iter().any(|unit| {
12138                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
12139                })
12140            );
12141        }
12142        assert!(
12143            declarations
12144                .iter()
12145                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
12146        );
12147        assert!(
12148            declarations
12149                .iter()
12150                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
12151        );
12152        assert!(
12153            declarations
12154                .iter()
12155                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
12156        );
12157    }
12158
12159    #[test]
12160    fn explicit_global_member_definition_has_canonical_package_boundary() {
12161        let source = r#"
12162namespace arangodb::aql {
12163class ExecutionPlan {
12164 public:
12165  template<class... Args> Node* createNode(Args&&... args);
12166};
12167}
12168
12169template<class... Args>
12170Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
12171"#;
12172        let parsed = parse_cpp_declarations(source, "global-member.cpp");
12173
12174        assert!(parsed.declarations().iter().any(|unit| {
12175            unit.is_function()
12176                && unit.package_name() == "arangodb::aql"
12177                && unit.short_name() == "ExecutionPlan.createNode"
12178                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
12179        }));
12180    }
12181
12182    #[test]
12183    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
12184        let source = r#"
12185#ifndef TINYXML2_INCLUDED
12186#define TINYXML2_INCLUDED
12187namespace tinyxml2 {
12188class TINYXML2_LIB XMLUtil {
12189 public:
12190  static const char* SkipWhiteSpace(const char* p) {
12191    while (*p) {
12192      if (*p == ' ') {
12193        ++p;
12194      }
12195    }
12196    return p;
12197  }
12198  static bool StringEqual(const char* p, const char* q) {
12199    return p == q;
12200  }
12201  class TINYXML2_LIB Helper {
12202   public:
12203    void Touch();
12204  };
12205  static void ToStr(int value, char* buffer);
12206 private:
12207  static const char* writeBoolTrue;
12208};
12209
12210class TINYXML2_LIB XMLNode {
12211 public:
12212  virtual XMLNode* ShallowClone() const = 0;
12213  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
12214};
12215}
12216#endif
12217"#;
12218        let mut parser = tree_sitter::Parser::new();
12219        parser
12220            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12221            .unwrap();
12222        let tree = parser.parse(source, None).unwrap();
12223        let mut boundary_found = false;
12224        walk_named_tree_preorder(tree.root_node(), true, |node| {
12225            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
12226                && name == "XMLUtil"
12227            {
12228                boundary_found = fragmented_export_sibling_class_boundary(node, source)
12229                    .and_then(|boundary| {
12230                        recover_exported_class_function_definition(boundary, source)
12231                    })
12232                    .is_some_and(|(_, name, _)| name == "XMLNode");
12233            }
12234            WalkControl::Continue
12235        });
12236        assert!(
12237            boundary_found,
12238            "fixture must exercise the recovered sibling boundary"
12239        );
12240
12241        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
12242        assert!(
12243            parsed
12244                .declarations()
12245                .iter()
12246                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
12247            "{:#?}",
12248            parsed.declarations()
12249        );
12250        assert!(
12251            parsed
12252                .declarations()
12253                .iter()
12254                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
12255            "{:#?}",
12256            parsed.declarations()
12257        );
12258        assert!(parsed.declarations().iter().any(|unit| {
12259            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
12260        }));
12261        assert!(
12262            parsed
12263                .declarations()
12264                .iter()
12265                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
12266        );
12267        assert!(
12268            parsed
12269                .declarations()
12270                .iter()
12271                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
12272        );
12273    }
12274
12275    #[test]
12276    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
12277        // Clang's diagnostic suite intentionally contains this ill-formed
12278        // spelling. The analyzer must retain the parser's explicit-global AST
12279        // boundary instead of constructing `cwg311::::cwg311::X`.
12280        let parsed = parse_cpp_declarations(
12281            r#"
12282namespace cwg311 {
12283namespace X { namespace Y {} }
12284namespace ::cwg311::X {}
12285}
12286"#,
12287            "explicit-global-namespace.cpp",
12288        );
12289
12290        assert!(parsed.declarations().iter().any(|unit| {
12291            unit.kind() == CodeUnitType::Module
12292                && unit.short_name() == "cwg311::X"
12293                && unit.fq_name() == "cwg311::X"
12294        }));
12295        assert!(
12296            parsed
12297                .declarations()
12298                .iter()
12299                .all(|unit| !unit.short_name().contains("::::")),
12300            "recovered namespace names must not retain empty scope components: {:#?}",
12301            parsed.declarations()
12302        );
12303    }
12304
12305    #[test]
12306    fn repeated_scope_separator_does_not_create_empty_function_owner() {
12307        let scope = ScopeInfo {
12308            package_name: "X".to_string(),
12309            module: None,
12310            class_unit: None,
12311            template_signature: None,
12312            template_metadata: None,
12313            declarations_are_fields: false,
12314            recovered_specialization_member_scope: false,
12315            visible_using_namespaces: Vec::new(),
12316        };
12317
12318        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
12319
12320        assert!(owner.is_none());
12321        assert_eq!(name, "doit");
12322        assert_eq!(package, "X");
12323    }
12324
12325    #[test]
12326    fn trailing_decltype_expression_is_not_a_function_declarator() {
12327        let source = r#"
12328namespace boost { namespace detail {
12329#if ! defined(BOOST_NO_SFINAE_EXPR) && \
12330    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
12331    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
12332#define BOOST_THREAD_PROVIDES_INVOKE
12333#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
12334template <class Fp, class A0, class ...Args>
12335inline auto
12336invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
12337       BOOST_THREAD_RV_REF(Args) ...args)
12338    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
12339{
12340    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
12341}
12342#endif
12343#endif
12344}}
12345"#;
12346        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
12347
12348        assert!(
12349            parsed
12350                .declarations()
12351                .iter()
12352                .all(|unit| unit.short_name() != ".*f")
12353        );
12354    }
12355
12356    fn find_class_named<'tree>(
12357        root: Node<'tree>,
12358        source: &str,
12359        expected_name: &str,
12360    ) -> Option<Node<'tree>> {
12361        let mut stack = vec![root];
12362        while let Some(node) = stack.pop() {
12363            if node.kind() == "class_specifier"
12364                && node
12365                    .child_by_field_name("name")
12366                    .is_some_and(|name| node_text(name, source) == expected_name)
12367            {
12368                return Some(node);
12369            }
12370            let mut cursor = node.walk();
12371            stack.extend(node.named_children(&mut cursor));
12372        }
12373        None
12374    }
12375
12376    #[test]
12377    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
12378        let source = r#"EXPORT void definition(struct Value value) {}
12379EXPORT void prototype(struct Value value);
12380"#;
12381        let mut parser = tree_sitter::Parser::new();
12382        parser
12383            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12384            .unwrap();
12385        let tree = parser.parse(source, None).unwrap();
12386        let root = tree.root_node();
12387        let mut cursor = root.walk();
12388        let callables = root
12389            .named_children(&mut cursor)
12390            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
12391            .collect::<Vec<_>>();
12392
12393        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
12394        for callable in callables {
12395            assert!(callable.has_error(), "fixture must exercise error recovery");
12396            assert!(
12397                cpp_sentinel_macro_parts(callable, source).is_none(),
12398                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
12399            );
12400        }
12401    }
12402
12403    #[test]
12404    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
12405        let source = r#"namespace absl {
12406ABSL_NAMESPACE_BEGIN
12407// Generate a floating-point variate conforming to a Beta distribution:
12408template <typename RealType = double>
12409class beta_distribution {
12410 public:
12411  using result_type = RealType;
12412
12413
12414  beta_distribution() : beta_distribution(1) {}
12415
12416  explicit beta_distribution(result_type alpha, result_type beta = 1)
12417      : param_(alpha, beta) {}
12418
12419  explicit beta_distribution(const param_type& p) : param_(p) {}
12420
12421  void reset() {}
12422
12423  // Generating functions
12424  template <typename URBG>
12425  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
12426    return (*this)(g, param_);
12427  }
12428
12429};
12430ABSL_NAMESPACE_END
12431}  // namespace absl
12432"#;
12433        let mut parser = tree_sitter::Parser::new();
12434        parser
12435            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12436            .unwrap();
12437        let tree = parser.parse(source, None).unwrap();
12438        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12439        let body = namespace
12440            .child_by_field_name("body")
12441            .expect("fixture namespace body");
12442        let sentinel = body.named_child(0).expect("sentinel envelope");
12443        let callable = sentinel
12444            .child_by_field_name("declarator")
12445            .and_then(extract_function_declarator)
12446            .and_then(cpp_function_declarator_name_node)
12447            .expect("preserved callable name");
12448
12449        assert_eq!(sentinel.kind(), "function_definition");
12450        assert_eq!(callable.kind(), "operator_name");
12451        assert!(
12452            cpp_sentinel_macro_parts(sentinel, source).is_some(),
12453            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
12454        );
12455    }
12456
12457    #[test]
12458    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
12459        let source = r#"namespace absl {
12460ABSL_NAMESPACE_BEGIN
12461// absl::discrete_distribution
12462//
12463// A discrete distribution produces random integers i, where 0 <= i < n
12464template <typename IntType = int>
12465class discrete_distribution {
12466 public:
12467  using result_type = IntType;
12468  class param_type {
12469   public:
12470    param_type() { init(); }
12471    template <typename InputIterator>
12472    explicit param_type(InputIterator begin, InputIterator end)
12473        : p_(begin, end) {
12474      init();
12475    }
12476  };
12477  discrete_distribution() : param_() {}
12478  explicit discrete_distribution(const param_type& p) : param_(p) {}
12479};
12480ABSL_NAMESPACE_END
12481}  // namespace absl
12482"#;
12483        let mut parser = tree_sitter::Parser::new();
12484        parser
12485            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12486            .unwrap();
12487        let tree = parser.parse(source, None).unwrap();
12488        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
12489        let body = namespace
12490            .child_by_field_name("body")
12491            .expect("fixture namespace body");
12492        let sentinel = body.named_child(0).expect("sentinel envelope");
12493        let callable = sentinel
12494            .child_by_field_name("declarator")
12495            .and_then(extract_function_declarator)
12496            .and_then(cpp_function_declarator_name_node)
12497            .expect("preserved callable name");
12498
12499        assert_eq!(sentinel.kind(), "function_definition");
12500        assert_eq!(callable.kind(), "identifier");
12501        assert!(
12502            cpp_sentinel_macro_parts(sentinel, source).is_some(),
12503            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
12504        );
12505    }
12506
12507    #[test]
12508    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
12509        let source = r#"
12510#define CPPCHECKLIB
12511class Library {
12512    struct Container {
12513        CPPCHECKLIB static std::string toString(Yield yield);
12514        CPPCHECKLIB static std::string toString(Action action);
12515    };
12516};
12517"#;
12518        let mut parser = tree_sitter::Parser::new();
12519        parser
12520            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12521            .unwrap();
12522        let tree = parser.parse(source, None).unwrap();
12523        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
12524        let parsed = parse_cpp_file(&file, source, &tree);
12525        assert!(
12526            parsed
12527                .declarations()
12528                .iter()
12529                .all(|unit| unit.fq_name() != "Library$Container.std"),
12530            "the qualified return-type namespace must not become a field: {:#?}",
12531            parsed.declarations()
12532        );
12533        for expected in ["(Yield)", "(Action)"] {
12534            assert!(
12535                parsed.declarations().iter().any(|unit| {
12536                    unit.is_function()
12537                        && unit.fq_name() == "Library$Container.toString"
12538                        && unit.signature() == Some(expected)
12539                }),
12540                "recovered toString overload {expected} is missing: {:#?}",
12541                parsed.declarations()
12542            );
12543        }
12544    }
12545
12546    #[test]
12547    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
12548        let source = r#"
12549#define SIMPLECPP_LIB
12550namespace simplecpp {
12551using TokenString = std::string;
12552struct Location { int line{}; };
12553class SIMPLECPP_LIB Token {
12554  TokenString prefix;
12555  void prefix_method() {}
12556 public:
12557  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12558      whitespaceahead(wsahead), location(loc), string(s)
12559      // The comment must not hide the constructor body from recovery.
12560      {
12561      flags();
12562  }
12563  TokenString string;
12564  bool whitespaceahead;
12565  Location location;
12566  Token *previous{};
12567 private:
12568  void flags() {
12569      whitespaceahead = true;
12570  }
12571};
12572}
12573"#;
12574        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
12575
12576        let location_fields = parsed
12577            .declarations()
12578            .iter()
12579            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
12580            .collect::<Vec<_>>();
12581        assert_eq!(
12582            location_fields.len(),
12583            1,
12584            "location should have one class-owned declaration: {:#?}",
12585            parsed.declarations()
12586        );
12587        assert!(
12588            location_fields[0].is_field(),
12589            "location has wrong kind: {:#?}",
12590            parsed.declarations()
12591        );
12592        assert!(
12593            parsed.declarations().iter().all(|unit| {
12594                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
12595            })
12596        );
12597        assert!(
12598            parsed.declarations().iter().all(|unit| {
12599                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12600            })
12601        );
12602        assert!(
12603            parsed
12604                .declarations()
12605                .iter()
12606                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
12607        );
12608        assert!(
12609            parsed
12610                .declarations()
12611                .iter()
12612                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
12613            "the recovered class must retain its constructor: {:#?}",
12614            parsed.declarations()
12615        );
12616        assert!(
12617            parsed
12618                .declarations()
12619                .iter()
12620                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
12621        );
12622        assert!(parsed.declarations().iter().any(|unit| {
12623            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
12624        }));
12625        let constructor = parsed
12626            .declarations()
12627            .iter()
12628            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
12629            .expect("recovered constructor");
12630        let constructor_start = source.find("Token(const").expect("constructor start");
12631        let constructor_end = source
12632            .get(
12633                ..source
12634                    .find("  TokenString string;")
12635                    .expect("constructor end"),
12636            )
12637            .expect("constructor slice")
12638            .trim_end()
12639            .len();
12640        assert!(
12641            parsed
12642                .navigation_ranges
12643                .get(constructor)
12644                .is_some_and(|ranges| {
12645                    ranges.iter().any(|range| {
12646                        range.start_byte == constructor_start && range.end_byte == constructor_end
12647                    })
12648                }),
12649            "constructor navigation must span the full body: {:#?}",
12650            parsed.navigation_ranges
12651        );
12652        assert_eq!(
12653            parsed
12654                .signature_metadata
12655                .get(constructor)
12656                .and_then(|metadata| metadata.first())
12657                .and_then(SignatureMetadata::callable_linkage),
12658            Some(CallableLinkage::External)
12659        );
12660        let token_class = parsed
12661            .declarations()
12662            .iter()
12663            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
12664            .expect("recovered Token class");
12665        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
12666        assert!(
12667            parsed
12668                .navigation_ranges
12669                .get(token_class)
12670                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
12671            "class navigation must include the terminating semicolon: {:#?}",
12672            parsed.navigation_ranges
12673        );
12674    }
12675
12676    #[test]
12677    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
12678        let source = r#"
12679#define SIMPLECPP_LIB
12680namespace simplecpp {
12681using TokenString = std::string;
12682class Macro;
12683struct Location {
12684  unsigned int fileIndex{};
12685  unsigned int line{};
12686  unsigned int col{};
12687};
12688struct Output {
12689  int type;
12690};
12691class SIMPLECPP_LIB Token {
12692 public:
12693  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
12694      whitespaceahead(wsahead), location(loc), string(s) {
12695      flags();
12696  }
12697  Token(const Token &tok) :
12698      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
12699      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
12700      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
12701  Token &operator=(const Token &tok) = delete;
12702  const TokenString& str() const { return string; }
12703  void setstr(const std::string &s) { string = s; flags(); }
12704  bool isOneOf(const char ops[]) const;
12705  TokenString macro;
12706  char op;
12707  bool comment;
12708  bool name;
12709  bool number;
12710  bool whitespaceahead;
12711  Location location;
12712  Token *previous{};
12713  Token *next{};
12714 private:
12715  void flags() {
12716      name = !string.empty();
12717      comment = false;
12718      number = false;
12719      op = 0;
12720  }
12721  TokenString string;
12722};
12723}
12724struct Following {
12725  int type;
12726};
12727class SIMPLECPP_LIB Later {
12728 public:
12729  Later(int value) : value(value) {}
12730  int value;
12731};
12732"#;
12733        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
12734        assert!(
12735            parsed
12736                .declarations()
12737                .iter()
12738                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
12739        );
12740        assert!(
12741            !parsed
12742                .declarations()
12743                .iter()
12744                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
12745        );
12746        assert!(
12747            parsed
12748                .declarations()
12749                .iter()
12750                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
12751        );
12752        assert!(
12753            !parsed
12754                .declarations()
12755                .iter()
12756                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
12757        );
12758        assert!(
12759            parsed
12760                .declarations()
12761                .iter()
12762                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
12763        );
12764        assert!(
12765            parsed
12766                .declarations()
12767                .iter()
12768                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
12769        );
12770        assert!(
12771            parsed
12772                .declarations()
12773                .iter()
12774                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
12775        );
12776        assert!(
12777            parsed
12778                .declarations()
12779                .iter()
12780                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
12781        );
12782        assert!(
12783            parsed
12784                .declarations()
12785                .iter()
12786                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
12787        );
12788        assert!(
12789            parsed
12790                .declarations()
12791                .iter()
12792                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
12793        );
12794        assert!(parsed.declarations().iter().all(|unit| {
12795            !matches!(
12796                unit.fq_name().as_str(),
12797                "simplecpp.Token.Following" | "simplecpp.Token.Later"
12798            )
12799        }));
12800        assert!(
12801            !parsed
12802                .declarations()
12803                .iter()
12804                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
12805            "the following struct must remain outside the recovered Token class"
12806        );
12807    }
12808
12809    #[test]
12810    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
12811        let source = r#"
12812#define SIMPLECPP_LIB
12813namespace {
12814namespace simplecpp {
12815using TokenString = std::string;
12816struct Location { int line{}; };
12817class SIMPLECPP_LIB HiddenToken {
12818 public:
12819  HiddenToken(const TokenString &s, const Location &loc) :
12820      location(loc), string(s) {
12821      flags();
12822  }
12823  TokenString string;
12824  Location location;
12825  HiddenToken *previous{};
12826 private:
12827  void flags() {}
12828};
12829}
12830}
12831"#;
12832        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
12833        let constructor = parsed
12834            .declarations()
12835            .iter()
12836            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
12837            .expect("recovered anonymous-namespace constructor");
12838        assert_eq!(
12839            parsed
12840                .signature_metadata
12841                .get(constructor)
12842                .and_then(|metadata| metadata.first())
12843                .and_then(SignatureMetadata::callable_linkage),
12844            Some(CallableLinkage::Internal)
12845        );
12846    }
12847
12848    #[test]
12849    fn macro_qualified_static_field_keeps_real_declarator() {
12850        let source = r#"#define JSON_INLINE_VARIABLE
12851struct Reader {
12852static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
12853static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
12854static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
12855};"#;
12856        let mut parser = tree_sitter::Parser::new();
12857        parser
12858            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12859            .unwrap();
12860        let tree = parser.parse(source, None).unwrap();
12861        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
12862        let parsed = parse_cpp_file(&file, source, &tree);
12863        for expected in [
12864            "Reader.npos",
12865            "Reader.other",
12866            "Reader.pointer",
12867            "Reader.reference",
12868        ] {
12869            assert!(
12870                parsed
12871                    .declarations()
12872                    .iter()
12873                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
12874                "real macro-decorated field {expected} is missing: {:#?}",
12875                parsed.declarations()
12876            );
12877        }
12878        assert!(
12879            parsed
12880                .declarations()
12881                .iter()
12882                .all(|unit| unit.fq_name() != "Reader.std"),
12883            "qualified type prefix became a pseudo-field: {:#?}",
12884            parsed.declarations()
12885        );
12886        let root = tree.root_node();
12887        let mut stack = vec![root];
12888        let mut signatures = Vec::new();
12889        while let Some(current) = stack.pop() {
12890            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
12891            {
12892                signatures.extend(
12893                    declarators
12894                        .into_iter()
12895                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
12896                );
12897            }
12898            let mut cursor = current.walk();
12899            stack.extend(current.named_children(&mut cursor));
12900        }
12901        signatures.sort();
12902        assert_eq!(
12903            signatures,
12904            [
12905                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
12906                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
12907                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
12908                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
12909            ]
12910        );
12911    }
12912
12913    fn member_function_linkage(source: &str) -> CallableLinkage {
12914        let mut parser = tree_sitter::Parser::new();
12915        parser
12916            .set_language(&tree_sitter_cpp::LANGUAGE.into())
12917            .unwrap();
12918        let tree = parser.parse(source, None).unwrap();
12919        let ancestry = ParentIndex::new(tree.root_node());
12920        let mut stack = vec![tree.root_node()];
12921        while let Some(node) = stack.pop() {
12922            if node.kind() == "function_definition" {
12923                let mut current = node.parent();
12924                while let Some(parent) = current {
12925                    if matches!(
12926                        parent.kind(),
12927                        "class_specifier" | "struct_specifier" | "union_specifier"
12928                    ) {
12929                        return cpp_callable_linkage(node, source, &ancestry);
12930                    }
12931                    current = parent.parent();
12932                }
12933            }
12934            let mut cursor = node.walk();
12935            stack.extend(node.named_children(&mut cursor));
12936        }
12937        panic!("fixture has no member function definition");
12938    }
12939
12940    #[test]
12941    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
12942        assert_eq!(
12943            member_function_linkage("struct Named { int method() { return 1; } };"),
12944            CallableLinkage::External
12945        );
12946        assert_eq!(
12947            member_function_linkage(
12948                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
12949            ),
12950            CallableLinkage::Internal
12951        );
12952        assert_eq!(
12953            member_function_linkage("struct { int method() { return 1; } } instance;"),
12954            CallableLinkage::Internal
12955        );
12956        assert_eq!(
12957            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
12958            CallableLinkage::Internal
12959        );
12960    }
12961
12962    #[test]
12963    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
12964        let source = r#"
12965#ifndef PROTON_VALUE_HPP
12966#define PROTON_VALUE_HPP
12967namespace proton {
12968namespace internal {
12969class value_base {
12970  protected:
12971    internal::data& data();
12972    internal::data data_;
12973  friend class codec::encoder;
12974  friend class codec::decoder;
12975};
12976}
12977class value : public internal::value_base, private internal::comparable<value> {
12978  private:
12979    template<class T, class U=void> struct assignable :
12980        public std::enable_if<codec::is_encodable<T>::value, U> {};
12981    template<class U> struct assignable<value, U> {};
12982  public:
12983    PN_CPP_EXTERN value();
12984    PN_CPP_EXTERN value(const value&);
12985    PN_CPP_EXTERN value& operator=(const value&);
12986    PN_CPP_EXTERN value(value&&);
12987    PN_CPP_EXTERN value& operator=(value&&);
12988    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
12989    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
12990        codec::encoder e(*this);
12991        e << x;
12992        return *this;
12993    }
12994    PN_CPP_EXTERN type_id type() const;
12995    PN_CPP_EXTERN bool empty() const;
12996    PN_CPP_EXTERN void clear();
12997    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
12998    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
12999  friend PN_CPP_EXTERN void swap(value&, value&);
13000  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
13001  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
13002  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
13003    value(pn_data_t* d);
13004    void reset(pn_data_t* d = 0);
13005};
13006}
13007#endif
13008"#;
13009        let mut parser = tree_sitter::Parser::new();
13010        parser
13011            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13012            .unwrap();
13013        let tree = parser.parse(source, None).unwrap();
13014        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
13015        let parsed = parse_cpp_file(&file, source, &tree);
13016        let macro_constructors = parsed
13017            .signature_metadata
13018            .iter()
13019            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
13020            .flat_map(|(_, metadata)| metadata)
13021            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
13022            .collect::<Vec<_>>();
13023
13024        assert_eq!(
13025            macro_constructors.len(),
13026            3,
13027            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
13028            parsed.declarations()
13029        );
13030        assert!(
13031            macro_constructors.iter().all(|metadata| {
13032                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
13033            }),
13034            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
13035        );
13036    }
13037
13038    #[test]
13039    fn recovered_export_class_typedef_uses_displaced_alias_name() {
13040        let source = r#"
13041namespace spi {
13042class Filter {
13043public:
13044    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
13045};
13046}
13047namespace filter {
13048class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
13049{
13050public:
13051    typedef spi::Filter BASE_CLASS;
13052    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
13053    BEGIN_LOG4CXX_CAST_MAP()
13054    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
13055    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
13056    END_LOG4CXX_CAST_MAP()
13057    FilterDecision decide() const;
13058};
13059}
13060"#;
13061        let mut parser = tree_sitter::Parser::new();
13062        parser
13063            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13064            .unwrap();
13065        let tree = parser.parse(source, None).unwrap();
13066        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
13067        let parsed = parse_cpp_file(&file, source, &tree);
13068        assert!(
13069            parsed.declarations().iter().any(|unit| {
13070                unit.is_class()
13071                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
13072                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
13073            }),
13074            "the displaced typedef alias must retain its declared name: {:#?}",
13075            parsed.declarations()
13076        );
13077        assert!(
13078            parsed
13079                .declarations()
13080                .iter()
13081                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
13082            "the qualified underlying type must not become a false nested alias: {:#?}",
13083            parsed.declarations()
13084        );
13085    }
13086
13087    #[test]
13088    fn exported_single_base_recovery_uses_displaced_class_name() {
13089        let source = r#"
13090class CORE_EXPORT QgsPoint : public AbstractGeometry
13091{
13092    Q_GADGET
13093
13094    Q_PROPERTY( double x READ x WRITE setX )
13095    Q_PROPERTY( double y READ y WRITE setY )
13096    Q_PROPERTY( double z READ z WRITE setZ )
13097    Q_PROPERTY( double m READ m WRITE setM )
13098
13099  public:
13100#ifndef SIP_RUN
13101    QgsPoint(
13102      double x = std::numeric_limits<double>::quiet_NaN(),
13103      double y = std::numeric_limits<double>::quiet_NaN(),
13104      double z = std::numeric_limits<double>::quiet_NaN(),
13105      double m = std::numeric_limits<double>::quiet_NaN(),
13106      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
13107    );
13108#else
13109    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 )];
13110    % MethodCode
13111    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13112    {
13113      int state;
13114      sipIsErr = 0;
13115      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13116      if ( !sipIsErr )
13117      {
13118        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13119      }
13120      sipReleaseType( p, sipType_QgsPointXY, state );
13121    }
13122    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
13123    {
13124      int state;
13125      sipIsErr = 0;
13126
13127      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
13128      if ( !sipIsErr )
13129      {
13130        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
13131      }
13132      sipReleaseType( p, sipType_QPointF, state );
13133    }
13134    else if (
13135      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
13136      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
13137      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
13138      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
13139    {
13140      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
13141      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
13142      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
13143      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
13144      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
13145      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
13146    }
13147    else // Invalid ctor arguments
13148    {
13149      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
13150      sipIsErr = 1;
13151    }
13152    % End
13153#endif
13154
13155    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
13156    explicit QgsPoint( QPointF p ) SIP_SKIP;
13157    explicit QgsPoint(
13158      Qgis::WkbType wkbType,
13159      double x = std::numeric_limits<double>::quiet_NaN(),
13160      double y = std::numeric_limits<double>::quiet_NaN(),
13161      double z = std::numeric_limits<double>::quiet_NaN(),
13162      double m = std::numeric_limits<double>::quiet_NaN()
13163    ) SIP_SKIP;
13164    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13165    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
13166    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
13167#ifndef SIP_RUN
13168  private:
13169    bool fuzzyHelper(
13170      double epsilon,
13171      const AbstractGeometry &other,
13172      bool is3DFlag,
13173      bool isMeasureFlag
13174    ) const
13175    {
13176      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
13177    }
13178#endif
13179};
13180class Ordinary : public Base { public: Ordinary(); };
13181class API_EXPORT Plain { public: Plain(); };
13182class API_EXPORT : public Base {};
13183class
13184PN_CPP_CLASS_EXTERN Sender : public Link {
13185    Sender();
13186};
13187class thread_ctx_t {};
13188class ctx_t ZMQ_FINAL : public thread_ctx_t {
13189    bool start();
13190};
13191"#;
13192        let mut parser = tree_sitter::Parser::new();
13193        parser
13194            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13195            .unwrap();
13196        let tree = parser.parse(source, None).unwrap();
13197        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
13198        let parsed = parse_cpp_file(&file, source, &tree);
13199        let declarations = parsed.declarations();
13200
13201        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
13202            assert!(
13203                declarations
13204                    .iter()
13205                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
13206                "missing recovered class {expected}: {declarations:#?}"
13207            );
13208        }
13209        let qgs_point = declarations
13210            .iter()
13211            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
13212            .expect("recovered QgsPoint class");
13213        assert_eq!(
13214            parsed.raw_supertypes.get(qgs_point),
13215            Some(&vec!["AbstractGeometry".to_string()]),
13216            "single-base export recovery must retain its displaced base"
13217        );
13218        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
13219        assert!(
13220            parsed
13221                .navigation_ranges
13222                .get(qgs_point)
13223                .is_some_and(|ranges| {
13224                    !ranges.is_empty()
13225                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
13226                }),
13227            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
13228            parsed.navigation_ranges.get(qgs_point)
13229        );
13230        let sender = declarations
13231            .iter()
13232            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
13233            .expect("recovered Sender class");
13234        assert_eq!(
13235            parsed.raw_supertypes.get(sender),
13236            Some(&vec!["Link".to_string()]),
13237            "post-declarator export recovery must retain its displaced base"
13238        );
13239        let ctx = declarations
13240            .iter()
13241            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
13242            .expect("recovered ctx_t class");
13243        assert_eq!(
13244            parsed.raw_supertypes.get(ctx),
13245            Some(&vec!["thread_ctx_t".to_string()]),
13246            "postfix export-macro recovery must retain its displaced base"
13247        );
13248        assert!(
13249            declarations.iter().any(|unit| {
13250                unit.is_function()
13251                    && unit.fq_name() == "QgsPoint.QgsPoint"
13252                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
13253            }),
13254            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
13255        );
13256        assert!(
13257            declarations.iter().all(|unit| {
13258                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
13259            }),
13260            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
13261        );
13262    }
13263
13264    #[test]
13265    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
13266        let positive_source =
13267            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
13268        let mut parser = tree_sitter::Parser::new();
13269        parser
13270            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13271            .unwrap();
13272        let positive_tree = parser.parse(positive_source, None).unwrap();
13273        assert!(cpp_reparsed_members_are_indexable(
13274            positive_tree.root_node(),
13275            positive_source
13276        ));
13277
13278        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
13279        let negative_tree = parser.parse(negative_source, None).unwrap();
13280        assert!(!cpp_reparsed_members_are_indexable(
13281            negative_tree.root_node(),
13282            negative_source
13283        ));
13284    }
13285
13286    #[test]
13287    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
13288        let copy_control_source = r#"
13289public:
13290    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
13291    explicit Token(const Token* tok);
13292    ~Token();
13293    Token* astOperand1() { return nullptr; }
13294"#;
13295        let constraint_source = r#"
13296private:
13297    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
13298    static T *tokAtImpl(T *tok, int index) {
13299        return tok;
13300    }
13301
13302    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
13303    static T *linkAtImpl(T *tok, int index) {
13304        return tok;
13305    }
13306
13307public:
13308    int late() const { return 1; }
13309"#;
13310        let mut parser = tree_sitter::Parser::new();
13311        parser
13312            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13313            .unwrap();
13314        let copy_control_tree = parser
13315            .parse(copy_control_source, None)
13316            .expect("parse copy-control fixture");
13317        assert!(
13318            copy_control_tree.root_node().has_error(),
13319            "fixture must exercise adjacent copy-control recovery"
13320        );
13321        assert!(
13322            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
13323            "a complete late getter must remain recoverable after adjacent copy-control declarations"
13324        );
13325        let mut cursor = copy_control_tree.root_node().walk();
13326        assert!(
13327            copy_control_tree
13328                .root_node()
13329                .named_children(&mut cursor)
13330                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
13331            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
13332            copy_control_tree.root_node().to_sexp()
13333        );
13334        let constraint_tree = parser
13335            .parse(constraint_source, None)
13336            .expect("parse constraint-macro fixture");
13337        assert!(constraint_tree.root_node().has_error());
13338        assert!(
13339            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
13340            "complete constraint-macro members must not hide a later ordinary member"
13341        );
13342        let mut cursor = constraint_tree.root_node().walk();
13343        assert!(
13344            constraint_tree
13345                .root_node()
13346                .named_children(&mut cursor)
13347                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
13348                    child,
13349                    constraint_source
13350                )),
13351            "fixture must retain the split constraint-macro prefix/function geometry"
13352        );
13353    }
13354
13355    #[test]
13356    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
13357        let source = r#"
13358struct Analyzer {
13359    struct Action {
13360        Action() = default;
13361        Action(const Action&) = default;
13362        Action& operator=(const Action& rhs) & = default;
13363
13364        template<class T,
13365                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
13366                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
13367        // NOLINTNEXTLINE(google-explicit-constructor)
13368        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
13369        {}
13370
13371        enum : std::uint16_t { None = 0, Read = (1 << 0) };
13372        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
13373
13374    private:
13375        unsigned int mFlag{};
13376    };
13377
13378    enum class Direction : unsigned char { Forward, Reverse };
13379    virtual Action analyze(Direction d) const = 0;
13380};
13381"#;
13382        let mut parser = tree_sitter::Parser::new();
13383        parser
13384            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13385            .unwrap();
13386        let tree = parser.parse(source, None).unwrap();
13387        assert!(tree.root_node().has_error());
13388        let root = tree.root_node();
13389        let outer = root
13390            .named_children(&mut root.walk())
13391            .find(|child| child.kind() == "ERROR")
13392            .expect("fragmented Analyzer prefix");
13393        let (_, outer_name, outer_fragment) = fragmented_plain_class_body(outer, source)
13394            .expect("structured Analyzer fragment boundary");
13395        assert_eq!(outer_name, "Analyzer");
13396        let outer_tree = cpp_reparse_fragmented_class_body(
13397            source,
13398            outer_fragment.reparse_start,
13399            outer_fragment.reparse_end,
13400        )
13401        .expect("reparse Analyzer body");
13402        let outer_root = outer_tree.root_node();
13403        let action_prefix = outer_root
13404            .named_children(&mut outer_root.walk())
13405            .find(|child| child.kind() == "ERROR")
13406            .expect("fragmented Action prefix");
13407        let (_, action_name, action_fragment) = fragmented_plain_class_body(action_prefix, source)
13408            .expect("structured Action fragment boundary");
13409        assert_eq!(action_name, "Action");
13410        let action_tree = cpp_reparse_fragmented_class_body(
13411            source,
13412            action_fragment.reparse_start,
13413            action_fragment.reparse_end,
13414        )
13415        .expect("reparse Action body");
13416        let action_root = action_tree.root_node();
13417        let macro_prefix = action_root
13418            .named_children(&mut action_root.walk())
13419            .find(|child| child.kind() == "ERROR")
13420            .expect("constraint macro prefix");
13421        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
13422            .expect("structured template macro prefix");
13423        let macro_companion =
13424            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
13425        assert!(
13426            cpp_reparsed_template_macro_constructor_companion_is_indexable(
13427                macro_companion,
13428                macro_parameter,
13429                source,
13430            ),
13431            "split constrained constructor must be admitted: {}",
13432            macro_companion.to_sexp()
13433        );
13434        assert!(
13435            cpp_reparsed_members_are_indexable(action_root, source),
13436            "complete Action body must pass the recovery gate: {}",
13437            action_tree.root_node().to_sexp()
13438        );
13439        assert!(
13440            cpp_reparsed_members_are_indexable(outer_root, source),
13441            "complete Analyzer body must pass the recovery gate: {}",
13442            outer_tree.root_node().to_sexp()
13443        );
13444        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
13445        let parsed = parse_cpp_file(&file, source, &tree);
13446        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
13447            assert!(
13448                parsed
13449                    .declarations()
13450                    .iter()
13451                    .any(|unit| unit.fq_name() == expected),
13452                "missing recovered declaration {expected}: {:#?}",
13453                parsed.declarations()
13454            );
13455        }
13456        assert!(
13457            parsed
13458                .declarations()
13459                .iter()
13460                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
13461            "nested members must not remain flattened: {:#?}",
13462            parsed.declarations()
13463        );
13464    }
13465
13466    #[test]
13467    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
13468        let source = r#"
13469raw_hash_set& operator=(raw_hash_set&& that) {
13470  return move_assign(
13471      std::move(that),
13472      typename AllocTraits::propagate_on_container_move_assignment());
13473}
13474
13475iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
13476  return {};
13477}
13478
13479void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
13480
13481iterator insert(const_iterator hint, value_type&& value)
13482    ABSL_ATTRIBUTE_LIFETIME_BOUND {
13483  return {};
13484}
13485
13486friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
13487  return left.size() == right.size();
13488}
13489
13490static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
13491  return static_cast<slot_type*>(buffer);
13492}
13493
13494protected:
13495// Included-range recovery can attach this comment to the template prefix.
13496template <class K>
13497void AssertOnFind([[maybe_unused]] const K& key) {
13498  Check(key);
13499}
13500"#;
13501        let mut parser = tree_sitter::Parser::new();
13502        parser
13503            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13504            .unwrap();
13505        let tree = parser.parse(source, None).unwrap();
13506        assert!(
13507            tree.root_node().has_error(),
13508            "the fixture must exercise tree-sitter's errorful member shapes"
13509        );
13510        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
13511
13512        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
13513        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
13514        assert!(!cpp_reparsed_members_are_indexable(
13515            incomplete_tree.root_node(),
13516            incomplete_source
13517        ));
13518
13519        let outside_error_source = "int foo() stray_attribute {}\n";
13520        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
13521        assert!(outside_error_tree.root_node().has_error());
13522        assert!(!cpp_reparsed_members_are_indexable(
13523            outside_error_tree.root_node(),
13524            outside_error_source
13525        ));
13526
13527        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
13528        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13529        assert!(!cpp_reparsed_members_are_indexable(
13530            variable_initializer_tree.root_node(),
13531            variable_initializer_source
13532        ));
13533    }
13534
13535    #[test]
13536    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
13537        let positive_source = r#"
13538std::pair<iterator, bool> insert(init_type&& value)
13539    ABSL_ATTRIBUTE_LIFETIME_BOUND
13540#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13541  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13542#endif
13543{
13544  return emplace(std::move(value));
13545}
13546"#;
13547        let mut parser = tree_sitter::Parser::new();
13548        parser
13549            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13550            .unwrap();
13551        let positive_tree = parser.parse(positive_source, None).unwrap();
13552        assert!(
13553            positive_tree.root_node().has_error(),
13554            "the fixture must exercise the split attribute/requires shape"
13555        );
13556        assert!(cpp_reparsed_members_are_indexable(
13557            positive_tree.root_node(),
13558            positive_source
13559        ));
13560
13561        let template_return_source = r#"
13562pair<int> insert(init_type&& value)
13563    ABSL_ATTRIBUTE_LIFETIME_BOUND
13564#if LANGUAGE_LEVEL >= 202002L
13565  requires(!Predicate<init_type>::value)
13566#endif
13567// Attributes and the function body may be separated by comments.
13568{
13569  return {};
13570}
13571"#;
13572        let template_return_tree = parser.parse(template_return_source, None).unwrap();
13573        assert!(
13574            cpp_reparsed_members_are_indexable(
13575                template_return_tree.root_node(),
13576                template_return_source
13577            ),
13578            "template-return attribute/requires tree: {}",
13579            template_return_tree.root_node().to_sexp()
13580        );
13581
13582        let no_body_source = r#"
13583std::pair<iterator, bool> insert(init_type&& value)
13584    ABSL_ATTRIBUTE_LIFETIME_BOUND
13585#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
13586  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
13587#endif
13588+ 0;
13589"#;
13590        let no_body_tree = parser.parse(no_body_source, None).unwrap();
13591        assert!(!cpp_reparsed_members_are_indexable(
13592            no_body_tree.root_node(),
13593            no_body_source
13594        ));
13595
13596        let extra_payload_source = r#"
13597pair<int> insert(init_type&& value)
13598    ABSL_ATTRIBUTE_LIFETIME_BOUND
13599#if LANGUAGE_LEVEL >= 202002L
13600  int unrelated;
13601  requires(Predicate<init_type>::value)
13602#endif
13603{
13604  return {};
13605}
13606"#;
13607        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
13608        assert!(!cpp_reparsed_members_are_indexable(
13609            extra_payload_tree.root_node(),
13610            extra_payload_source
13611        ));
13612
13613        let variable_initializer_source = r#"
13614int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
13615#if LANGUAGE_LEVEL >= 202002L
13616  requires(true)
13617#endif
13618{
13619  bad;
13620}
13621"#;
13622        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
13623        assert!(!cpp_reparsed_members_are_indexable(
13624            variable_initializer_tree.root_node(),
13625            variable_initializer_source
13626        ));
13627    }
13628
13629    #[test]
13630    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
13631        let source = r#"namespace absl {
13632ABSL_NAMESPACE_BEGIN namespace container_internal {
13633
13634class raw_hash_set : public Base {
13635 public:
13636  using value_type = int;
13637
13638  template <class U,
13639            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
13640  void insert(U value) { (void)value; }
13641
13642  struct InsertSlot {
13643    raw_hash_set& s;
13644  };
13645};
13646
13647}
13648ABSL_NAMESPACE_END
13649}"#;
13650        let mut parser = tree_sitter::Parser::new();
13651        parser
13652            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13653            .unwrap();
13654        let tree = parser.parse(source, None).unwrap();
13655        let root = tree.root_node();
13656        let outer_namespace = root
13657            .named_children(&mut root.walk())
13658            .find(|child| child.kind() == "namespace_definition")
13659            .expect("outer absl namespace");
13660        let declaration_list = outer_namespace
13661            .child_by_field_name("body")
13662            .expect("outer namespace body");
13663        let sentinel_function = declaration_list
13664            .named_children(&mut declaration_list.walk())
13665            .find(|child| child.kind() == "function_definition")
13666            .expect("malformed namespace sentinel function");
13667        let ancestry = ParentIndex::new(root);
13668        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
13669            .expect("structured nested namespace sentinel");
13670        let fragmented =
13671            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
13672                .expect("fragmented raw_hash_set class");
13673        assert_eq!(fragmented.class_node.kind(), "ERROR");
13674        assert_eq!(fragmented.name, "raw_hash_set");
13675        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
13676
13677        let outer_scope =
13678            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
13679        let mut outer_siblings = Vec::new();
13680        push_cpp_sentinel_sibling_classes(
13681            &mut outer_siblings,
13682            declaration_list,
13683            sentinel.function,
13684            &outer_scope,
13685            source,
13686            &ancestry,
13687        );
13688        let [outer_shadow] = outer_siblings.as_slice() else {
13689            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
13690        };
13691        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
13692        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
13693
13694        let field = "    raw_hash_set& s;";
13695        let start = source.find(field).expect("InsertSlot field") + 4;
13696        let node = root
13697            .descendant_for_byte_range(start, start + "raw_hash_set".len())
13698            .expect("raw_hash_set type node");
13699        let recovered = cpp_sentinel_recovered_classes(root, source);
13700        let [deep_class] = recovered.as_slice() else {
13701            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
13702        };
13703        assert_eq!(
13704            deep_class.namespace_scope_components,
13705            vec!["absl", "container_internal"]
13706        );
13707        assert_eq!(
13708            deep_class.scope_components,
13709            vec!["absl", "container_internal", "raw_hash_set"]
13710        );
13711        assert!(
13712            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
13713                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
13714        );
13715
13716        assert_eq!(
13717            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
13718            Some(vec![
13719                "absl".to_string(),
13720                "container_internal".to_string(),
13721                "raw_hash_set".to_string(),
13722                "InsertSlot".to_string(),
13723            ])
13724        );
13725
13726        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
13727        let parsed = parse_cpp_file(&file, source, &tree);
13728        let raw_hash_set = parsed
13729            .declarations()
13730            .iter()
13731            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
13732            .expect("recovered raw_hash_set class");
13733        assert_eq!(
13734            raw_hash_set.fq_name(),
13735            "absl::container_internal.raw_hash_set",
13736            "the recovered declaration must publish under the deeper sentinel namespace"
13737        );
13738        assert_eq!(
13739            parsed.raw_supertypes.get(raw_hash_set),
13740            Some(&vec!["Base".to_string()]),
13741            "the structured base clause on the fragmented ERROR prefix must survive publication"
13742        );
13743        assert!(
13744            parsed.materialization_records.iter().any(|record| matches!(
13745                record,
13746                MaterializationRecord::RecoveredDeclaration { recovery, unit }
13747                    if unit == raw_hash_set && *recovery == deep_class.class_range
13748            )),
13749            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
13750            parsed.materialization_records
13751        );
13752    }
13753
13754    /// Issue #2358: recording an aggregate definition must not walk the whole
13755    /// file.
13756    ///
13757    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
13758    /// class-like shape that has a body, so the removal step runs once per
13759    /// aggregate. It used to `retain` over `top_level_declarations` and over
13760    /// *every* child list in the file on each of those calls, comparing whole
13761    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
13762    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
13763    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
13764    /// tens of thousands of times over and the C forward differential never
13765    /// finished.
13766    ///
13767    /// A definition the file has not already declared removes nothing, so the
13768    /// honest cost is zero regardless of how many other aggregates surround it.
13769    /// Two sizes an order of magnitude apart pin that the count is not merely
13770    /// small but independent of the file.
13771    ///
13772    /// The declaration walk answers every ancestor question from a
13773    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
13774    /// the root for each one (#2361). Substituting the index is only safe
13775    /// because it answers the identical question, so pin that on the shapes
13776    /// this file's recovery paths care about: anonymous and named aggregates,
13777    /// nested namespaces, templates, macro-displaced declarations and the
13778    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
13779    /// too -- `Node::parent` walks the visible tree, not the named one.
13780    #[test]
13781    fn the_parent_index_answers_what_tree_sitter_answers() {
13782        const SHAPES: [&str; 5] = [
13783            "namespace outer { namespace inner { struct Tag { int field; }; } }",
13784            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
13785            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
13786            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
13787            "class API Broken : public First, public Second {\n  void member();\n",
13788        ];
13789        for source in SHAPES {
13790            let mut parser = tree_sitter::Parser::new();
13791            parser
13792                .set_language(&tree_sitter_cpp::LANGUAGE.into())
13793                .unwrap();
13794            let tree = parser.parse(source, None).unwrap();
13795            let root = tree.root_node();
13796            let ancestry = ParentIndex::new(root);
13797            let mut nodes = 0usize;
13798            let mut stack = vec![root];
13799            while let Some(node) = stack.pop() {
13800                nodes += 1;
13801                assert_eq!(
13802                    node.parent().map(|parent| parent.id()),
13803                    ancestry.parent(node).map(|parent| parent.id()),
13804                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
13805                );
13806                let mut cursor = node.walk();
13807                stack.extend(node.children(&mut cursor));
13808            }
13809            assert!(nodes > 1, "{source:?} produced no tree to compare");
13810        }
13811    }
13812
13813    /// Forward declarations followed by definitions are compacted as one
13814    /// batch, without rescanning the shared namespace/top-level lists for each
13815    /// tag. Definitions are intentionally visited in reverse order so the
13816    /// assertion also pins eager remove-and-reappend ordering.
13817    #[test]
13818    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
13819        for aggregates in [64usize, 512] {
13820            let mut source =
13821                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
13822            for index in 0..aggregates {
13823                writeln!(source, "struct tag{index};").unwrap();
13824            }
13825            for index in (0..aggregates).rev() {
13826                writeln!(
13827                    source,
13828                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
13829                )
13830                .unwrap();
13831            }
13832            source.push_str("}\n");
13833
13834            start_code_unit_removal_scan_probe();
13835            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
13836            let scanned = finish_code_unit_removal_scan_probe();
13837
13838            let expected_names: Vec<String> = (0..aggregates)
13839                .rev()
13840                .map(|index| format!("tag{index}"))
13841                .collect();
13842            let top_level_names: Vec<String> = parsed
13843                .top_level_declarations
13844                .iter()
13845                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13846                .map(|unit| unit.short_name().to_string())
13847                .collect();
13848            let namespace = parsed
13849                .declarations()
13850                .iter()
13851                .find(|unit| {
13852                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
13853                })
13854                .expect("generated namespace should be declared");
13855            let child_names: Vec<String> = parsed.children[namespace]
13856                .iter()
13857                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13858                .map(|unit| unit.short_name().to_string())
13859                .collect();
13860            assert_eq!(
13861                aggregates,
13862                parsed
13863                    .declarations()
13864                    .iter()
13865                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
13866                    .count(),
13867                "every aggregate must still be declared at {aggregates} aggregates"
13868            );
13869            assert_eq!(expected_names, top_level_names);
13870            assert_eq!(expected_names, child_names);
13871            assert_eq!(
13872                0, scanned,
13873                "replacing {aggregates} forward declarations must compact their shared lists once"
13874            );
13875        }
13876    }
13877
13878    #[test]
13879    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
13880        const DISTINCT_PER_KIND: usize = 64;
13881        let mut source = String::new();
13882        for index in 0..DISTINCT_PER_KIND {
13883            writeln!(source, "typedef int Alias{index};").unwrap();
13884        }
13885        writeln!(source, "typedef long Alias0;").unwrap();
13886        for index in 0..DISTINCT_PER_KIND {
13887            writeln!(source, "#define MACRO_{index} {index}").unwrap();
13888        }
13889        writeln!(source, "#define MACRO_0 duplicate").unwrap();
13890        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
13891
13892        let mut parser = tree_sitter::Parser::new();
13893        parser
13894            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13895            .unwrap();
13896        let tree = parser.parse(&source, None).unwrap();
13897        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
13898
13899        start_declaration_identity_comparison_probe();
13900        let parsed = parse_cpp_file(&file, &source, &tree);
13901        let comparisons = finish_declaration_identity_comparison_probe();
13902
13903        assert_eq!(
13904            DISTINCT_PER_KIND + 1,
13905            parsed
13906                .declarations()
13907                .iter()
13908                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
13909                .count(),
13910            "every physical typedef alias declaration must be retained so \
13911             conditional branch guards stay available to the resolver"
13912        );
13913        assert_eq!(
13914            DISTINCT_PER_KIND,
13915            parsed
13916                .declarations()
13917                .iter()
13918                .filter(|unit| {
13919                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
13920                })
13921                .count(),
13922            "macros should retain semantic-identity deduplication"
13923        );
13924        assert_eq!(
13925            2,
13926            parsed
13927                .declarations()
13928                .iter()
13929                .filter(|unit| {
13930                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
13931                })
13932                .count(),
13933            "function overloads must remain distinct"
13934        );
13935
13936        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
13937        assert!(
13938            comparisons <= dedup_inputs * 4,
13939            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
13940        );
13941    }
13942
13943    #[test]
13944    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
13945        let source = r#"namespace absl {
13946ABSL_NAMESPACE_BEGIN namespace container_internal {
13947template <typename T>
13948class broken {
13949 public:
13950  using value_type = T;
13951  T operator->() const { return &operator*(); }
13952  using alias = value_type;
13953};
13954}
13955}
13956"#;
13957        let mut parser = tree_sitter::Parser::new();
13958        parser
13959            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13960            .unwrap();
13961        let tree = parser.parse(source, None).unwrap();
13962        let broken = find_class_named(tree.root_node(), source, "broken")
13963            .expect("the positive fixture must expose the broken class node");
13964        assert!(
13965            broken.has_error(),
13966            "the positive fixture must retain an internal parser error"
13967        );
13968        assert!(
13969            cpp_complete_class_body_close(broken).is_some(),
13970            "the positive fixture must expose a real class body close"
13971        );
13972        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
13973        assert!(
13974            recovered.iter().any(|class| {
13975                class.scope_components == ["absl", "container_internal", "broken"]
13976            }),
13977            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
13978        );
13979    }
13980
13981    #[test]
13982    fn sentinel_recovery_keeps_members_after_nested_body_close() {
13983        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
13984NLOHMANN_BASIC_JSON_TPL_DECLARATION
13985class basic_json {
13986 private:
13987  union storage {
13988    int value;
13989  } data;
13990 public:
13991  using late_alias = int;
13992  late_alias value() const;
13993};
13994NLOHMANN_JSON_NAMESPACE_END
13995"#;
13996        let mut parser = tree_sitter::Parser::new();
13997        parser
13998            .set_language(&tree_sitter_cpp::LANGUAGE.into())
13999            .unwrap();
14000        let tree = parser.parse(source, None).unwrap();
14001        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14002        let basic_json = recovered
14003            .iter()
14004            .find(|class| {
14005                class
14006                    .scope_components
14007                    .last()
14008                    .is_some_and(|name| name == "basic_json")
14009            })
14010            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
14011        let late_alias = source
14012            .find("late_alias value")
14013            .expect("late alias reference");
14014        assert!(
14015            basic_json.class_range.start_byte < late_alias
14016                && late_alias < basic_json.class_range.end_byte,
14017            "the recovered class range must include members after a nested close: {basic_json:#?}"
14018        );
14019    }
14020
14021    #[test]
14022    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
14023        let source = r#"namespace absl {
14024ABSL_NAMESPACE_BEGIN namespace container_internal {
14025template <typename T>
14026class broken {
14027 public:
14028  using value_type = T;
14029  T operator->() const { return &operator*(); }
14030}
14031}
14032"#;
14033        let mut parser = tree_sitter::Parser::new();
14034        parser
14035            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14036            .unwrap();
14037        let tree = parser.parse(source, None).unwrap();
14038        let broken = find_class_named(tree.root_node(), source, "broken")
14039            .expect("the negative fixture must expose the malformed class node");
14040        assert!(
14041            broken.has_error(),
14042            "the negative fixture must retain a parser error"
14043        );
14044        assert!(
14045            cpp_complete_class_body_close(broken).is_none(),
14046            "the malformed class must not expose a real body close"
14047        );
14048        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14049        assert!(
14050            recovered
14051                .iter()
14052                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
14053            "an incomplete class must not borrow the namespace close: {recovered:#?}"
14054        );
14055    }
14056
14057    #[test]
14058    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
14059        let source = r#"namespace absl {
14060ABSL_NAMESPACE_BEGIN namespace container_internal {
14061template <typename T>
14062struct broken {
14063  using value_type = T;
14064};
14065}
14066
14067#ifdef OWNER_DEF
14068template <typename T>
14069typename broken<T>::value_type broken<T>::method() {
14070  value_type value{};
14071  return value;
14072}
14073#endif
14074
14075namespace sibling {
14076template <typename T>
14077typename broken<T>::value_type broken<T>::other() {
14078  value_type value{};
14079  return value;
14080}
14081}
14082
14083ABSL_NAMESPACE_END
14084}
14085"#;
14086        let mut parser = tree_sitter::Parser::new();
14087        parser
14088            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14089            .unwrap();
14090        let tree = parser.parse(source, None).unwrap();
14091        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14092        let broken = recovered
14093            .iter()
14094            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
14095            .expect("the sentinel class must be recovered");
14096        let method_start = source
14097            .find("typename broken<T>::value_type broken<T>::method()")
14098            .expect("guarded sibling owner");
14099        let method_end = source[method_start..]
14100            .find("\n}")
14101            .map(|offset| method_start + offset + 2)
14102            .expect("guarded sibling owner close");
14103        assert!(
14104            broken
14105                .owner_ranges
14106                .iter()
14107                .any(|owner| owner.range.start_byte <= method_start
14108                    && method_end <= owner.range.end_byte),
14109            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
14110        );
14111        let sibling_start = source
14112            .find("typename broken<T>::value_type broken<T>::other()")
14113            .expect("nested namespace sibling owner");
14114        assert!(
14115            broken
14116                .owner_ranges
14117                .iter()
14118                .all(|owner| owner.range.start_byte > sibling_start
14119                    || owner.range.end_byte <= sibling_start),
14120            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
14121        );
14122    }
14123
14124    #[test]
14125    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
14126        let source = r#"#ifdef OUTER
14127namespace absl {
14128ABSL_NAMESPACE_BEGIN namespace container_internal {
14129template <typename T>
14130struct broken {
14131  using value_type = T;
14132};
14133}
14134}
14135
14136#ifdef OWNER_DEF
14137template <typename T>
14138typename broken<T>::value_type broken<T>::method() {
14139  value_type value{};
14140  return value;
14141}
14142#endif
14143#endif
14144"#;
14145        let mut parser = tree_sitter::Parser::new();
14146        parser
14147            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14148            .unwrap();
14149        let tree = parser.parse(source, None).unwrap();
14150        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
14151        let broken = recovered
14152            .iter()
14153            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
14154            .expect("the sentinel class must be recovered");
14155        let method_start = source
14156            .find("typename broken<T>::value_type broken<T>::method()")
14157            .expect("outer sibling owner");
14158        assert!(
14159            broken
14160                .owner_ranges
14161                .iter()
14162                .all(|owner| owner.range.start_byte > method_start
14163                    || owner.range.end_byte <= method_start),
14164            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
14165        );
14166    }
14167
14168    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
14169    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
14170        let mut signatures = parsed
14171            .declarations()
14172            .iter()
14173            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
14174            .filter_map(|unit| unit.signature().map(str::to_string))
14175            .collect::<Vec<_>>();
14176        signatures.sort();
14177        signatures.dedup();
14178        signatures
14179    }
14180
14181    #[test]
14182    fn callable_parameter_types_come_from_the_ast_parameter_list() {
14183        let source = r#"
14184template <typename T, ENABLE_BYTES(T)>
14185Vec256<T> DupOdd(Vec256<T> value) { return value; }
14186
14187struct Visitor {
14188  void fail(this auto const& self) {}
14189};
14190"#;
14191        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
14192        let dup_odd = parsed
14193            .declarations()
14194            .iter()
14195            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
14196            .expect("DupOdd declaration");
14197        assert_eq!(
14198            dup_odd.signature(),
14199            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
14200        );
14201        assert_eq!(
14202            parsed
14203                .signature_metadata
14204                .get(dup_odd)
14205                .and_then(|metadata| metadata.first())
14206                .and_then(SignatureMetadata::callable_parameter_types),
14207            Some(["Vec256<T>".to_string()].as_slice())
14208        );
14209
14210        let fail = parsed
14211            .declarations()
14212            .iter()
14213            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
14214            .expect("explicit-object member");
14215        assert_eq!(fail.signature(), Some("(const this auto &)"));
14216        let metadata = parsed
14217            .signature_metadata
14218            .get(fail)
14219            .and_then(|metadata| metadata.first())
14220            .expect("explicit-object signature metadata");
14221        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
14222        assert!(
14223            metadata
14224                .callable_arity()
14225                .is_some_and(|arity| arity.accepts(0))
14226        );
14227    }
14228
14229    #[test]
14230    fn trailing_qualifiers_survive_parameter_list_whitespace() {
14231        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
14232        // declarator's structure, so an out-of-line definition that spells its
14233        // parameter list with different whitespace than the declaration must
14234        // still carry it.
14235        let source = r#"
14236struct Widget {
14237  bool multiline(int settings, int supprs) const;
14238  bool doublespace(int settings, int supprs) const;
14239  bool noexcept_multiline(int settings, int supprs) noexcept;
14240  bool ref_multiline(int settings, int supprs) &&;
14241};
14242bool
14243Widget::multiline (int settings,
14244                   int supprs) const
14245{ return settings + supprs > 0; }
14246bool Widget::doublespace(int settings,  int supprs) const { return true; }
14247bool Widget::noexcept_multiline(int settings,
14248                                int supprs) noexcept { return true; }
14249bool Widget::ref_multiline(int settings,
14250                           int supprs) && { return true; }
14251"#;
14252        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
14253        assert_eq!(
14254            vec!["(int, int) const".to_string()],
14255            identity_signatures(&parsed, "Widget.multiline")
14256        );
14257        assert_eq!(
14258            vec!["(int, int) const".to_string()],
14259            identity_signatures(&parsed, "Widget.doublespace")
14260        );
14261        assert_eq!(
14262            vec!["(int, int) noexcept".to_string()],
14263            identity_signatures(&parsed, "Widget.noexcept_multiline")
14264        );
14265        assert_eq!(
14266            vec!["(int, int) &&".to_string()],
14267            identity_signatures(&parsed, "Widget.ref_multiline")
14268        );
14269    }
14270
14271    #[test]
14272    fn macro_fragmented_plain_class_keeps_following_member_signature() {
14273        let source = r#"
14274struct CString {};
14275class CMessage {
14276public:
14277  CString GetParams(unsigned int index, unsigned int length = -1) const
14278      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
14279    return GetParamsColon(index, length);
14280  }
14281  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
14282};
14283CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
14284  return {};
14285}
14286"#;
14287        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
14288        assert_eq!(
14289            vec!["(unsigned int, unsigned int) const".to_string()],
14290            identity_signatures(&parsed, "CMessage.GetParamsColon")
14291        );
14292    }
14293
14294    #[test]
14295    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
14296        let source = r#"
14297#pragma once
14298#define DEMO_DEPRECATED(message)
14299namespace demo {
14300struct Base {
14301    static int aligned(int value) { return value; }
14302    int legacy(int value) const
14303        DEMO_DEPRECATED("use replacement()") { return value; }
14304    int replacement() const;
14305    void run(int value);
14306};
14307struct OtherBase {
14308    void run(int value);
14309    static int aligned(int value) { return value; }
14310};
14311struct Derived : Base {};
14312struct Override : Base {
14313    void run(int value);
14314    static int aligned(int value) { return value; }
14315};
14316struct RecoveredOverride : Base {
14317    int legacy(int value) const
14318        DEMO_DEPRECATED("use replacement()") { return value; }
14319    void run(int value);
14320};
14321struct Hidden : Base {
14322    void run(int first, int second);
14323    static int aligned(int first, int second) { return first + second; }
14324};
14325struct Ambiguous : Base, OtherBase {};
14326}
14327struct Global {};
14328"#;
14329        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
14330        let declarations = parsed.declarations();
14331        let fq_names = declarations
14332            .iter()
14333            .map(|unit| unit.fq_name())
14334            .collect::<std::collections::BTreeSet<_>>();
14335
14336        for expected in [
14337            "demo.Base",
14338            "demo.Base.aligned",
14339            "demo.Base.legacy",
14340            "demo.Base.replacement",
14341            "demo.Base.run",
14342            "demo.Derived",
14343            "demo.OtherBase",
14344            "demo.Override",
14345            "demo.RecoveredOverride",
14346            "demo.Hidden",
14347            "demo.Ambiguous",
14348            "Global",
14349        ] {
14350            assert!(
14351                fq_names.contains(expected),
14352                "missing {expected} from namespaced macro fragment: {declarations:#?}"
14353            );
14354        }
14355        assert!(
14356            !fq_names.contains("Derived"),
14357            "following class escaped its namespace: {declarations:#?}"
14358        );
14359        assert!(
14360            !fq_names.contains("demo.Global"),
14361            "global class crossed the recovered namespace boundary: {declarations:#?}"
14362        );
14363    }
14364
14365    #[test]
14366    fn trailing_qualifiers_still_separate_genuine_overloads() {
14367        // The qualifier must keep distinguishing the real C++ overload sets it
14368        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
14369        let source = r#"
14370struct Widget {
14371  int* slot(int index);
14372  const int* slot(int index) const;
14373  int log(int severity) &;
14374  int log(int severity) &&;
14375};
14376"#;
14377        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
14378        assert_eq!(
14379            vec!["(int)".to_string(), "(int) const".to_string()],
14380            identity_signatures(&parsed, "Widget.slot")
14381        );
14382        assert_eq!(
14383            vec!["(int) &".to_string(), "(int) &&".to_string()],
14384            identity_signatures(&parsed, "Widget.log")
14385        );
14386    }
14387
14388    #[test]
14389    fn virtual_specifier_is_not_part_of_the_identity_signature() {
14390        // `override` never appears on the out-of-line definition, and C++ does
14391        // not make it part of the signature, so it must not split the identity.
14392        let source = r#"
14393struct Base {
14394  virtual void run(int value) const;
14395};
14396struct Widget : Base {
14397  void run(int value) const override;
14398};
14399void Widget::run(int value) const {}
14400"#;
14401        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
14402        assert_eq!(
14403            vec!["(int) const".to_string()],
14404            identity_signatures(&parsed, "Widget.run")
14405        );
14406    }
14407
14408    #[test]
14409    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
14410        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
14411        // the function type, so a declaration that spells `const int` and a
14412        // definition that spells `int` are one entity.
14413        let source = r#"
14414struct Widget {
14415  bool value_params(const int settings, const int supprs);
14416  void pointee_const(const int* p);
14417  void pointer_const(int* const p);
14418  void both_const(const int* const p);
14419  void reference_const(const int& p);
14420  void array_const(const int values[4]);
14421};
14422bool Widget::value_params(int settings, int supprs) { return true; }
14423void Widget::pointer_const(int* p) {}
14424void Widget::both_const(const int* p) {}
14425"#;
14426        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
14427        assert_eq!(
14428            vec!["(int, int)".to_string()],
14429            identity_signatures(&parsed, "Widget.value_params")
14430        );
14431        assert_eq!(
14432            vec!["(int *)".to_string()],
14433            identity_signatures(&parsed, "Widget.pointer_const")
14434        );
14435        assert_eq!(
14436            vec!["(const int *)".to_string()],
14437            identity_signatures(&parsed, "Widget.both_const")
14438        );
14439        // The const that is not top-level still distinguishes the type.
14440        assert_eq!(
14441            vec!["(const int *)".to_string()],
14442            identity_signatures(&parsed, "Widget.pointee_const")
14443        );
14444        assert_eq!(
14445            vec!["(const int &)".to_string()],
14446            identity_signatures(&parsed, "Widget.reference_const")
14447        );
14448        assert_eq!(
14449            vec!["(const int [4])".to_string()],
14450            identity_signatures(&parsed, "Widget.array_const")
14451        );
14452    }
14453
14454    #[test]
14455    fn top_level_parameter_const_still_separates_pointee_overloads() {
14456        let source = r#"
14457struct Widget {
14458  void take(const int* p);
14459  void take(int* p);
14460};
14461"#;
14462        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
14463        assert_eq!(
14464            vec!["(const int *)".to_string(), "(int *)".to_string()],
14465            identity_signatures(&parsed, "Widget.take")
14466        );
14467    }
14468
14469    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
14470        let mut parser = tree_sitter::Parser::new();
14471        parser
14472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
14473            .unwrap();
14474        let tree = parser.parse(source, None).unwrap();
14475        let start = source.find(callable_name).expect("callable declaration");
14476        let declarator =
14477            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
14478        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
14479    }
14480
14481    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
14482        let mut shapes = comparable_shapes(source, callable_name);
14483        assert_eq!(1, shapes.len(), "{shapes:?}");
14484        match shapes.remove(0) {
14485            CppComparableSlot::Shape(shape) => shape,
14486            other => panic!("expected a comparable shape, got {other:?}"),
14487        }
14488    }
14489
14490    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
14491        let mut current = shape.root();
14492        loop {
14493            match shape.node(current) {
14494                CppComparableNode::Named { .. } => return shape.node(current),
14495                CppComparableNode::Pointer { inner, .. }
14496                | CppComparableNode::Reference { inner }
14497                | CppComparableNode::Array { inner } => current = *inner,
14498                CppComparableNode::Generic { base, .. } => current = *base,
14499            }
14500        }
14501    }
14502
14503    #[test]
14504    fn comparable_shape_keeps_pointee_const() {
14505        assert_ne!(
14506            sole_comparable_shape("void f(const char* p);", "f("),
14507            sole_comparable_shape("void f(char* p);", "f(")
14508        );
14509    }
14510
14511    #[test]
14512    fn comparable_shape_keeps_inner_pointer_const() {
14513        assert_ne!(
14514            sole_comparable_shape("void f(int** p);", "f("),
14515            sole_comparable_shape("void f(int* const* p);", "f(")
14516        );
14517    }
14518
14519    #[test]
14520    fn comparable_shape_drops_top_level_pointer_const() {
14521        assert_eq!(
14522            sole_comparable_shape("void f(int* const p);", "f("),
14523            sole_comparable_shape("void f(int* p);", "f(")
14524        );
14525    }
14526
14527    #[test]
14528    fn comparable_shape_drops_top_level_base_const() {
14529        assert_eq!(
14530            sole_comparable_shape("void f(const int p);", "f("),
14531            sole_comparable_shape("void f(int p);", "f(")
14532        );
14533    }
14534
14535    #[test]
14536    fn comparable_shape_decays_top_level_array_to_pointer() {
14537        assert_eq!(
14538            sole_comparable_shape("void f(int a[3]);", "f("),
14539            sole_comparable_shape("void f(int* a);", "f(")
14540        );
14541        assert_eq!(
14542            sole_comparable_shape("void f(int* a[3]);", "f("),
14543            sole_comparable_shape("void f(int** a);", "f(")
14544        );
14545    }
14546
14547    #[test]
14548    fn comparable_shape_keeps_array_behind_pointer() {
14549        assert_ne!(
14550            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
14551            sole_comparable_shape("struct S { void f(int** a); };", "f(")
14552        );
14553    }
14554
14555    #[test]
14556    fn comparable_shape_records_written_name_and_lexical_scope() {
14557        let declared =
14558            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
14559        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
14560        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
14561            panic!("named leaf");
14562        };
14563        assert_eq!(["Msg".to_string()].as_slice(), name.path());
14564        assert_eq!(
14565            ["ns".to_string(), "S".to_string()].as_slice(),
14566            name.lexical_scope()
14567        );
14568        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
14569            panic!("named leaf");
14570        };
14571        assert_eq!(
14572            ["ns".to_string(), "Msg".to_string()].as_slice(),
14573            name.path()
14574        );
14575        assert!(name.lexical_scope().is_empty());
14576        assert_ne!(declared, defined);
14577    }
14578
14579    #[test]
14580    fn comparable_shape_marks_sized_primitive_leaf() {
14581        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
14582        let CppComparableNode::Named {
14583            name, primitive, ..
14584        } = comparable_named_leaf(&shape)
14585        else {
14586            panic!("named leaf");
14587        };
14588        assert!(primitive);
14589        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
14590        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
14591    }
14592
14593    #[test]
14594    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
14595        assert_eq!(
14596            vec![CppComparableSlot::Unstructured],
14597            comparable_shapes("void f(void (*cb)(int));", "f(")
14598        );
14599    }
14600
14601    #[test]
14602    fn comparable_shape_reports_ellipsis_slot() {
14603        let shapes = comparable_shapes("void f(int a, ...);", "f(");
14604        assert_eq!(2, shapes.len(), "{shapes:?}");
14605        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
14606    }
14607
14608    #[test]
14609    fn comparable_shape_keeps_template_argument_const() {
14610        assert_ne!(
14611            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
14612            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
14613        );
14614    }
14615
14616    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
14617    /// file-scope tag that a later `struct inner *` at file scope may name.
14618    #[test]
14619    fn c_file_mints_aggregate_member_tag_at_file_scope() {
14620        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14621        let parsed = parse_cpp_declarations(source, "x.c");
14622        let declarations = parsed.declarations();
14623
14624        assert!(
14625            declarations
14626                .iter()
14627                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14628            "expected a file-scope inner tag, got {declarations:?}"
14629        );
14630        assert!(
14631            declarations
14632                .iter()
14633                .all(|unit| unit.fq_name() != "outer$inner"),
14634            "expected no nested identity, got {declarations:?}"
14635        );
14636        assert!(
14637            declarations
14638                .iter()
14639                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
14640        );
14641        // Members still belong to their own aggregate.
14642        assert!(
14643            declarations
14644                .iter()
14645                .any(|unit| unit.fq_name() == "inner.value")
14646        );
14647        assert!(
14648            declarations
14649                .iter()
14650                .any(|unit| unit.fq_name() == "outer.item")
14651        );
14652
14653        let outer = declarations
14654            .iter()
14655            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
14656            .expect("outer");
14657        assert!(
14658            parsed
14659                .children
14660                .get(outer)
14661                .into_iter()
14662                .flatten()
14663                .all(|child| child.fq_name() != "inner"),
14664            "the tag must not hang off the aggregate it is written inside: {:?}",
14665            parsed.children
14666        );
14667    }
14668
14669    /// A header carries no compilation language of its own, and a `.cpp`
14670    /// translation unit really does declare a nested class. Both keep exactly
14671    /// the C++ extraction they had before the C dialect existed.
14672    #[test]
14673    fn header_and_cpp_files_keep_nested_tag_identity() {
14674        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14675        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
14676            let parsed = parse_cpp_declarations(source, name);
14677            let declarations = parsed.declarations();
14678            assert!(
14679                declarations
14680                    .iter()
14681                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14682                "{name} must keep the nested identity, got {declarations:?}"
14683            );
14684            assert!(
14685                declarations.iter().all(|unit| unit.fq_name() != "inner"),
14686                "{name} must not mint a file-scope tag, got {declarations:?}"
14687            );
14688            assert!(
14689                declarations
14690                    .iter()
14691                    .any(|unit| unit.fq_name() == "outer$inner.value")
14692            );
14693        }
14694    }
14695
14696    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
14697    #[test]
14698    fn uppercase_c_extension_keeps_cpp_tag_scope() {
14699        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
14700        let parsed = parse_cpp_declarations(source, "x.C");
14701        assert!(
14702            parsed
14703                .declarations()
14704                .iter()
14705                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
14706        );
14707    }
14708
14709    /// There is no such thing as a partially nested tag in C: every level of a
14710    /// nested aggregate chain lands at the same enclosing scope.
14711    #[test]
14712    fn c_file_mints_every_nesting_level_at_file_scope() {
14713        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
14714        let parsed = parse_cpp_declarations(source, "z.c");
14715        let declarations = parsed.declarations();
14716
14717        for tag in ["a", "b", "c"] {
14718            assert!(
14719                declarations
14720                    .iter()
14721                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
14722                "expected a file-scope {tag}, got {declarations:?}"
14723            );
14724        }
14725        assert!(
14726            declarations
14727                .iter()
14728                .all(|unit| !unit.fq_name().contains('$')),
14729            "no level may keep a nested identity, got {declarations:?}"
14730        );
14731        // Each member still belongs to the aggregate that declares it.
14732        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
14733        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
14734        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
14735    }
14736
14737    /// An enum tag is a tag; its enumerators stay members of the enum, which is
14738    /// what makes them ordinary identifiers at the enum's own (file) scope.
14739    #[test]
14740    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
14741        let source = "struct outer { enum color { RED, GREEN } c; };\n";
14742        let parsed = parse_cpp_declarations(source, "e.c");
14743        let declarations = parsed.declarations();
14744
14745        let color = declarations
14746            .iter()
14747            .find(|unit| unit.is_class() && unit.fq_name() == "color")
14748            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
14749        assert!(
14750            declarations
14751                .iter()
14752                .all(|unit| unit.fq_name() != "outer$color")
14753        );
14754        for enumerator in ["color.RED", "color.GREEN"] {
14755            assert!(
14756                declarations.iter().any(|unit| unit.fq_name() == enumerator),
14757                "expected {enumerator}, got {declarations:?}"
14758            );
14759        }
14760        let children = parsed
14761            .children
14762            .get(color)
14763            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
14764        assert!(
14765            ["color.RED", "color.GREEN"]
14766                .iter()
14767                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
14768            "enumerators must hang off their enum: {children:?}"
14769        );
14770    }
14771
14772    #[test]
14773    fn c_file_mints_member_list_union_at_file_scope() {
14774        let source = "struct outer { union inner { int a; float b; } item; };\n";
14775        let parsed = parse_cpp_declarations(source, "u.c");
14776        let declarations = parsed.declarations();
14777        assert!(
14778            declarations
14779                .iter()
14780                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
14781            "expected a file-scope inner union, got {declarations:?}"
14782        );
14783        assert!(
14784            declarations
14785                .iter()
14786                .all(|unit| unit.fq_name() != "outer$inner")
14787        );
14788        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
14789        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
14790    }
14791
14792    /// A tag declared in a namespace member list is not a file-scope tag: the
14793    /// nearest enclosing non-aggregate scope is the namespace.
14794    #[test]
14795    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
14796        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
14797        let parsed = parse_cpp_declarations(source, "n.c");
14798        let declarations = parsed.declarations();
14799        let inner = declarations
14800            .iter()
14801            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
14802            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
14803        assert_eq!(inner.package_name(), "ns");
14804        assert!(
14805            declarations
14806                .iter()
14807                .all(|unit| unit.fq_name() != "ns.outer$inner")
14808        );
14809    }
14810
14811    /// Pins today's treatment of a tag declared inside a function body: the
14812    /// declaration walk does not descend into statement bodies, so no unit is
14813    /// minted for it in either dialect. C block scope is out of scope for the
14814    /// dialect change, and this test proves the change did not disturb it.
14815    #[test]
14816    fn function_local_tags_are_unchanged_in_both_dialects() {
14817        let source =
14818            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
14819        for name in ["y.c", "y.cpp"] {
14820            let parsed = parse_cpp_declarations(source, name);
14821            let declarations = parsed.declarations();
14822            assert!(
14823                declarations
14824                    .iter()
14825                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
14826                "{name}: {declarations:?}"
14827            );
14828            for tag in ["localtag", "deeper", "localtag$deeper"] {
14829                assert!(
14830                    declarations.iter().all(|unit| unit.fq_name() != tag),
14831                    "{name} must not mint {tag}, got {declarations:?}"
14832                );
14833            }
14834        }
14835    }
14836
14837    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
14838    /// re-scope: the typedef name is identical in both dialects.
14839    #[test]
14840    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
14841        let source = "typedef struct { int v; } T;\n";
14842        for name in ["t.c", "t.cpp"] {
14843            let parsed = parse_cpp_declarations(source, name);
14844            let declarations = parsed.declarations();
14845            assert!(
14846                declarations
14847                    .iter()
14848                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
14849                "{name}: {declarations:?}"
14850            );
14851        }
14852    }
14853
14854    #[test]
14855    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
14856        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
14857        let parsed = parse_cpp_declarations(source, "socket.c");
14858        let declarations = parsed.declarations();
14859        assert_eq!(
14860            declarations
14861                .iter()
14862                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
14863                .count(),
14864            1,
14865            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
14866        );
14867        for expected in [
14868            "PAL_HANDLE",
14869            "PAL_HANDLE.sock",
14870            "PAL_HANDLE$sock",
14871            "PAL_HANDLE$sock.ops",
14872        ] {
14873            assert!(
14874                declarations.iter().any(|unit| unit.fq_name() == expected),
14875                "expected {expected}, got {declarations:?}"
14876            );
14877        }
14878    }
14879
14880    /// `class` is not C. Source that spells one in a `.c` file is not C code,
14881    /// so it keeps the C++ reading rather than acquiring a half-C identity.
14882    #[test]
14883    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
14884        let source = "class outer { class inner { int v; }; };\n";
14885        let c_parsed = parse_cpp_declarations(source, "k.c");
14886        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
14887        let c_declarations = c_parsed.declarations();
14888        let cpp_declarations = cpp_parsed.declarations();
14889        assert!(
14890            c_declarations
14891                .iter()
14892                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
14893            "{c_declarations:?}"
14894        );
14895        assert_eq!(
14896            c_declarations
14897                .iter()
14898                .map(|unit| unit.fq_name())
14899                .collect::<std::collections::BTreeSet<_>>(),
14900            cpp_declarations
14901                .iter()
14902                .map(|unit| unit.fq_name())
14903                .collect::<std::collections::BTreeSet<_>>()
14904        );
14905    }
14906}