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 crate::graph::resolver::OrphanedNamespaceScopeIndex;
8use crate::graph::syntax::{MacroReplacementField, ObjectMacroReplacement};
9use brokk_bifrost_core::analyzer::common::{
10    node_source_text, parse_source_ranges_with_cancellation, parse_source_region,
11};
12use brokk_bifrost_core::analyzer::fq_name::{
13    FqName, SegmentId, SegmentKind, joined_segments, normalize_joined, segment_interner,
14};
15use brokk_bifrost_core::analyzer::model::{
16    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
17    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
18    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
19    ParameterMetadata, Range, SignatureMetadata, StructuredTypeIdentity,
20    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
21};
22use brokk_bifrost_core::analyzer::parsed_file::ParsedFile;
23use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
24use brokk_bifrost_core::analyzer::structural::materialization::{
25    GenerationKind, MaterializationRecord,
26};
27use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
28use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
29use brokk_bifrost_core::analyzer::tree_walk::{
30    NodeKindIds, ParentIndex, WalkControl, children_iter, named_children_iter,
31    push_children_reversed, push_named_children_reversed, walk_named_tree_preorder,
32};
33use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
34use brokk_bifrost_core::hash::{HashMap, HashSet};
35use regex::Regex;
36use tree_sitter::{Node, Parser, Tree};
37
38/// Intern one qualified-name segment in the process-global interner.
39fn cpp_segment(text: &str, kind: SegmentKind) -> SegmentId {
40    segment_interner().intern(text, kind)
41}
42
43/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
44/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
45/// `::` head is exactly the mixed-separator store issue #1163 is about; the
46/// structured form records each namespace component, and the equivalence check
47/// renders it natively (with `::` between adjacent Package segments) so it
48/// round-trips to the legacy string. Splitting the already-joined string here is
49/// the M1 bridge — the legacy strings stay authoritative until M3.
50fn cpp_push_package(fq: &mut FqName, package_name: &str) {
51    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
52        fq.push(cpp_segment(component, SegmentKind::Package));
53    }
54}
55
56/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
57const CPP_PACKAGE_SEPARATOR: &str = "::";
58
59/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
60/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
61/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
62/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
63/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
64/// cpp-specific native rendering rule is needed for this chain.
65fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
66    let mut first = true;
67    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
68    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
69    // entry point, not re-inference of an already-structured name.
70    for component in chain.split('$').filter(|c| !c.is_empty()) {
71        let kind = if first {
72            SegmentKind::Type
73        } else {
74            SegmentKind::Nested
75        };
76        fq.push(cpp_segment(component, kind));
77        first = false;
78    }
79}
80
81/// Structured name for a C++ namespace module: every `::`-separated component is
82/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
83/// `short_name` with an empty `package_name`).
84fn cpp_namespace_fq(full_name: &str) -> FqName {
85    let mut fq = FqName::new();
86    cpp_push_package(&mut fq, full_name);
87    fq
88}
89
90/// The per-level namespace components a `namespace_definition`'s `name` field
91/// declares.
92///
93/// A C++17 nested definition (`namespace a::b::c`) parses as a
94/// `nested_namespace_specifier` whose named children are the per-level
95/// `namespace_identifier`s plus, for three or more levels, a further
96/// `nested_namespace_specifier`; the `::` separators, the optional per-level
97/// `inline`, and the leading global `::` are all anonymous tokens the walk
98/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
99/// segment path as the expanded `namespace a { namespace b { } }` form.
100///
101/// A shape outside that grammar is the deliberately ill-formed source the
102/// diagnostic corpora carry. Those keep their historical single-component
103/// reading of the raw name text, which the caller still joins to the lexical
104/// namespace exactly as before.
105fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
106    let mut components = Vec::new();
107    let mut stack = vec![node];
108    while let Some(current) = stack.pop() {
109        match current.kind() {
110            "namespace_identifier" | "identifier" => {
111                components.push(normalize_cpp_whitespace(node_text(current, source)));
112            }
113            "nested_namespace_specifier" => {
114                for index in (0..current.named_child_count()).rev() {
115                    stack.push(
116                        current
117                            .named_child(index)
118                            .expect("index below the node's own named child count"),
119                    );
120                }
121            }
122            _ => return cpp_raw_namespace_name_components(node, source),
123        }
124    }
125    if components.iter().any(String::is_empty) {
126        return cpp_raw_namespace_name_components(node, source);
127    }
128    components
129}
130
131/// The historical reading of a namespace name node: its whole source text as
132/// one component, with a leading global `::` marker dropped so the caller's
133/// global-scope handling stays the AST boundary rather than a text prefix.
134///
135/// This is the recovery path for source outside the C++ grammar, so the text it
136/// returns can carry a separator that names nothing. react-native-windows
137/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
138/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
139/// trailing separator with no tail. The caller stores this component in
140/// `short_name` and derives the fq by splitting it back apart, so an empty tail
141/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
142/// here, at the one place the malformed text enters, rather than leaving each
143/// consumer to guard (#2353, a variant of #1878).
144fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
145    let start = node
146        .child(0)
147        .filter(|child| !child.is_named() && child.kind() == "::")
148        .map_or(node.start_byte(), |marker| marker.end_byte());
149    let text = normalize_cpp_whitespace(
150        source
151            .get(start..node.end_byte())
152            .expect("namespace name node covers one source range"),
153    );
154    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
155    if text.is_empty() {
156        return Vec::new();
157    }
158    vec![text]
159}
160
161/// Return the named namespace path that structurally encloses `node`.
162///
163/// This intentionally follows namespace AST ancestors rather than inspecting
164/// source text. Anonymous namespaces are not representable in the legacy C++
165/// package field, so a path containing one fails closed.
166fn cpp_lexical_namespace_name<'tree>(
167    node: Node<'tree>,
168    source: &str,
169    ancestry: &ParentIndex<'tree>,
170) -> Option<String> {
171    let mut components = Vec::new();
172    let mut ancestor = ancestry.parent(node);
173    while let Some(current) = ancestor {
174        if current.kind() == "namespace_definition" {
175            let name_node = current.child_by_field_name("name")?;
176            let name = normalize_cpp_whitespace(node_text(name_node, source));
177            if name.is_empty() {
178                return None;
179            }
180            components.push(name);
181        }
182        ancestor = ancestry.parent(current);
183    }
184    if components.is_empty() {
185        return None;
186    }
187    components.reverse();
188    // Same shared empty-component decision as `cpp_push_package`, which splits
189    // this string back apart: an enclosing namespace whose own name node is
190    // malformed contributes a component carrying its own separator (#2353).
191    Some(
192        normalize_joined(
193            &components.join(CPP_PACKAGE_SEPARATOR),
194            CPP_PACKAGE_SEPARATOR,
195        )
196        .into_owned(),
197    )
198}
199
200/// Nested-class `$` join for short names. An anonymous parent class (empty
201/// short_name) contributes no segment: the FqName bridge drops empty
202/// components, so a bare `parent$child` join would desync `short_name` from
203/// the fq and trip the package/short boundary assert in
204/// `CodeUnit::with_signature_and_fq` (#2140).
205fn cpp_join_nested_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/// Member `.` join for short names; same anonymous-parent guard as
214/// [`cpp_join_nested_short`] (#2140).
215fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
216    if parent_short.is_empty() {
217        name.to_string()
218    } else {
219        format!("{parent_short}.{name}")
220    }
221}
222
223/// Structural fq for a leaf declaration: the parent unit's fq plus this
224/// declaration's own name as one segment (or the package segments plus the
225/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
226/// chain, so a literal `$` inside a source identifier (Cython template
227/// substitution points, gcc `$`-identifiers) survives instead of corrupting
228/// the chain and tripping the package/short boundary assert (#2140).
229fn cpp_leaf_fq(
230    package_name: &str,
231    parent: Option<&CodeUnit>,
232    name: &str,
233    kind_if_nested: SegmentKind,
234    kind_if_top: SegmentKind,
235) -> FqName {
236    if let Some(parent) = parent {
237        parent
238            .fq()
239            .clone()
240            .with_pushed(cpp_segment(name, kind_if_nested))
241    } else {
242        let mut fq = FqName::new();
243        cpp_push_package(&mut fq, package_name);
244        fq.push(cpp_segment(name, kind_if_top));
245        fq
246    }
247}
248
249/// Structured name for a member unit (function, field, enumerator). The
250/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
251/// the member has an owner, by `.member`; free functions and globals have no
252/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
253/// C++ member names never contain a literal `.`, so the single `.` (if any)
254/// separates the owner chain from the member.
255pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
256    let mut fq = FqName::new();
257    cpp_push_package(&mut fq, package_name);
258    match short_name.rsplit_once('.') {
259        Some((owner_chain, member)) => {
260            cpp_push_type_chain(&mut fq, owner_chain);
261            fq.push(cpp_segment(member, SegmentKind::Member));
262        }
263        None => fq.push(cpp_segment(short_name, SegmentKind::Member)),
264    }
265    fq
266}
267
268/// Structured name for a rendered C++ qualified name supplied at the input
269/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
270///
271/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
272/// it hands back one component for a nested-class chain (`Outer$Inner`). An
273/// exact-name lookup then asks the store for owner `ns` with identifier
274/// `Outer$Inner`, while the declaration persists as a
275/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
276/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
277/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
278/// boundary identical to the persisted one. A `$` that is *not* a nesting join
279/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
280/// and stays reachable through the identifier fallback, which seeks the whole
281/// component text as one spelling.
282pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
283    let mut fq = FqName::new();
284    for component in parse_symbol_path(Language::Cpp, rendered) {
285        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
286        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
287        // construction boundary `cpp_push_type_chain` owns for extraction.
288        let mut first = true;
289        for part in component.split('$').filter(|part| !part.is_empty()) {
290            let kind = if first {
291                // A client-typed head claims no kind, exactly as
292                // `parse_symbol_path_fq` records it.
293                SegmentKind::Unknown
294            } else {
295                SegmentKind::Nested
296            };
297            fq.push(cpp_segment(part, kind));
298            first = false;
299        }
300    }
301    fq
302}
303
304/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
305///
306/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
307/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
308/// `Top.Only`, and the exact-name retain drops the persisted nested identity
309/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
310/// placements extraction stores, so each exact query's parent and terminal
311/// match a persisted row and the structural retain keeps it.
312///
313/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
314/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
315/// is one of those: the `::` there is the package join the canonical name
316/// already spells.
317pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
318    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
319        return Vec::new();
320    }
321    let texts = parse_symbol_path(Language::Cpp, rendered);
322    if texts.len() < 2 {
323        return Vec::new();
324    }
325    cpp_qualified_kind_interpretations(&texts)
326}
327
328/// Every namespace / type / member boundary a `::`-joined source spelling can
329/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
330/// segments.
331fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
332    let len = texts.len();
333    let mut names = Vec::new();
334    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
335    for packages in 0..len {
336        let rest = len - packages;
337        let mut type_chain = Vec::with_capacity(len);
338        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
339        type_chain.push(SegmentKind::Type);
340        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
341        names.push(cpp_fq_with_kinds(texts, type_chain));
342        if rest >= 2 {
343            let mut member = Vec::with_capacity(len);
344            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
345            member.push(SegmentKind::Type);
346            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
347            member.push(SegmentKind::Member);
348            names.push(cpp_fq_with_kinds(texts, member));
349        } else if packages >= 1 {
350            let mut function = vec![SegmentKind::Package; packages];
351            function.push(SegmentKind::Member);
352            names.push(cpp_fq_with_kinds(texts, function));
353        }
354    }
355    names
356}
357
358fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
359    assert_eq!(
360        texts.len(),
361        kinds.len(),
362        "a qualified interpretation names every segment"
363    );
364    let mut fq = FqName::new();
365    for (text, kind) in texts.iter().zip(kinds) {
366        fq.push(cpp_segment(text, kind));
367    }
368    fq
369}
370
371#[derive(Clone)]
372pub struct ScopeInfo {
373    package_name: String,
374    module: Option<CodeUnit>,
375    class_unit: Option<CodeUnit>,
376    template_signature: Option<String>,
377    template_metadata: Option<CppTemplateMetadata>,
378    declarations_are_fields: bool,
379    recovered_specialization_member_scope: bool,
380    /// Namespace targets of every `using namespace X;` directive lexically
381    /// visible at this point in the file (declaration order), threaded
382    /// forward sibling-by-sibling by the sequential container walk (see
383    /// `CppWork::Siblings`). An out-of-line member definition written as a
384    /// bare `Class::method` at file/namespace scope with no enclosing
385    /// `namespace {}` block (issue #1093, e.g. log4cxx's
386    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
387    /// const { ... }`) has no other structural signal for which namespace
388    /// actually owns `Class`; this is the best-effort candidate list used to
389    /// recover it so the definition's indexed identity matches its header
390    /// declaration's.
391    visible_using_namespaces: Vec<String>,
392}
393
394struct CppContainer<'tree> {
395    node: Node<'tree>,
396    scope: ScopeInfo,
397}
398
399struct CppNodeWork<'tree> {
400    node: Node<'tree>,
401    scope: ScopeInfo,
402}
403
404/// Cursor over one container's remaining named children, processed one at a
405/// time (rather than all at once) so a `using namespace X;` sibling can
406/// update `scope.visible_using_namespaces` for the siblings that follow it,
407/// matching real C++ using-directive semantics. Nested container work is
408/// still pushed and fully drained before the cursor resumes (stack LIFO
409/// order), preserving the original left-to-right visitation order.
410struct CppSiblingsWork<'tree> {
411    children: std::vec::IntoIter<Node<'tree>>,
412    scope: ScopeInfo,
413}
414
415enum CppWork<'tree> {
416    Container(CppContainer<'tree>),
417    Node(CppNodeWork<'tree>),
418    Siblings(CppSiblingsWork<'tree>),
419}
420
421fn class_like_name<'tree>(
422    node: Node<'tree>,
423    source: &str,
424    ancestry: &ParentIndex<'tree>,
425) -> Option<String> {
426    let best = class_like_name_from_children(node, source);
427    if let Some(parent) = ancestry.parent(node)
428        && matches!(
429            parent.kind(),
430            "declaration" | "field_declaration" | "function_definition"
431        )
432        // A class_specifier carrying its own body proves the grammar name is
433        // the real class name: a sibling declarator then declares an object
434        // (`class X {} x;`), never a displaced class name. The gate matters
435        // when the class name is itself an all-caps token (`X`, `API`) --
436        // without it the export-macro re-read below steals the object
437        // declarator's name for the class (#2283). The genuine export-macro
438        // shapes leave the class_specifier bodyless, the same invariant
439        // recover_malformed_exported_multiple_base_class already gates on.
440        && cpp_body_node(node).is_none()
441        && node
442            .child_by_field_name("name")
443            .map(|name_node| {
444                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
445            })
446            .unwrap_or(false)
447        && let Some(recovered) = exported_class_name_from_node(parent, source)
448        && best.as_deref() != Some(recovered.as_str())
449    {
450        return Some(recovered);
451    }
452    best.or_else(|| {
453        node.child_by_field_name("name")
454            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
455            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
456    })
457}
458
459fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
460    let mut grammar_name = None;
461    if let Some(name_node) = node.child_by_field_name("name") {
462        let name = normalize_cpp_whitespace(node_text(name_node, source));
463        if name.is_empty() {
464            return None;
465        }
466        if !cpp_export_macro_token(&name) {
467            return Some(name);
468        }
469        grammar_name = Some(name);
470    }
471
472    let mut best = None;
473    let mut cursor = node.walk();
474    let mut stack = Vec::new();
475    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
476        if matches!(
477            child.kind(),
478            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
479        ) {
480            break;
481        }
482        stack.push(child);
483    }
484
485    while let Some(current) = stack.pop() {
486        if matches!(current.kind(), "type_identifier" | "identifier") {
487            let name = normalize_cpp_whitespace(node_text(current, source));
488            if !name.is_empty() && !cpp_export_macro_token(&name) {
489                best = Some(name);
490            }
491            continue;
492        }
493
494        push_named_children_reversed(current, &mut stack);
495    }
496    best.or(grammar_name)
497}
498
499pub fn cpp_export_macro_token(token: &str) -> bool {
500    token
501        .chars()
502        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
503}
504
505struct RecoveredExportedClass<'tree> {
506    declaration_node: Node<'tree>,
507    name: String,
508    body: Option<Node<'tree>>,
509    raw_supertypes: Option<Vec<String>>,
510    uses_initializer_body: bool,
511    /// Present only for the fragmented multiple-base export shape (issue #938).
512    /// Carries the true class-body byte region -- the members tree-sitter scattered
513    /// out of the recovered node -- so they can be reparsed and re-owned as members
514    /// rather than lost inside the truncated `initializer_list` stand-in.
515    fragmented_body: Option<FragmentedExportBody>,
516}
517
518struct RecoveredFunctionLikeExportClassPair {
519    name: String,
520    range: Range,
521    raw_supertypes: Option<Vec<String>>,
522    fragmented_body: FragmentedExportBody,
523}
524
525struct RecoveredEmbeddedFunctionLikeExportClass {
526    name: String,
527    range: Range,
528    raw_supertypes: Vec<String>,
529    fragmented_body: FragmentedExportBody,
530}
531
532/// The recovered class-body geometry for a fragmented multiple-base export class.
533/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
534/// verbatim for a region reparse (issue #941 machinery) so every recovered member
535/// keeps its exact original byte/line position. `class_range` is the full class
536/// navigation range spanning to the displaced closing brace.
537struct FragmentedExportBody {
538    reparse_start: usize,
539    reparse_end: usize,
540    class_range: Range,
541}
542
543fn recovered_fragmented_export_body(
544    body: Node<'_>,
545    class_range: Range,
546) -> Option<FragmentedExportBody> {
547    let open = body.child(0).filter(|child| child.kind() == "{")?;
548    let close = body
549        .child(body.child_count().saturating_sub(1))
550        .filter(|child| child.kind() == "}" && !child.is_missing());
551    Some(FragmentedExportBody {
552        reparse_start: open.end_byte(),
553        // A zero-width missing `}` contributes no source byte. Keep the whole
554        // body range in that case; subtracting one byte would discard the last
555        // member's semicolon and make the otherwise valid region unsafe to
556        // index. A real close token is excluded by its structured start.
557        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
558        class_range,
559    })
560}
561
562struct DisplacedFragmentNamespaceBoundary<'tree> {
563    class_close: Node<'tree>,
564    class_semicolon: Node<'tree>,
565    namespace_items: Vec<Node<'tree>>,
566}
567
568/// Result of validating a reparsed fragmented class body.  A complete tree can
569/// safely consume the whole region.  A partial tree may contain only the exact
570/// class-named constructor that tree-sitter merged into an access label; its
571/// remaining siblings must stay on the ordinary outer walk.
572enum FragmentedExportMembers {
573    Complete(Tree),
574    ConditionalConstructor(Tree),
575}
576
577#[derive(Clone, Copy)]
578struct DisplacedMacroClassTail {
579    split_index: usize,
580    class_range: Range,
581}
582
583fn recover_exported_class_declaration<'tree>(
584    node: Node<'tree>,
585    source: &str,
586) -> Option<RecoveredExportedClass<'tree>> {
587    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
588        return Some(recovered);
589    }
590
591    let class_node = first_class_like_child(node)?;
592    if let Some(name_node) = class_node.child_by_field_name("name") {
593        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
594        if cpp_export_macro_token(&class_name) {
595            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
596            // Name declarator. Only a bare declarator can be the displaced class name;
597            // wrappers describe an object whose type merely happens to look macro-like.
598            let mut cursor = node.walk();
599            if node
600                .children_by_field_name("declarator", &mut cursor)
601                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
602            {
603                return None;
604            }
605        } else if has_direct_cpp_declarator(node) {
606            return None;
607        }
608    }
609    let name = exported_class_name_from_node(class_node, source)?;
610    Some(RecoveredExportedClass {
611        declaration_node: class_node,
612        name,
613        body: cpp_body_node(class_node),
614        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
615            .then(|| extract_cpp_supertypes(class_node, source)),
616        uses_initializer_body: false,
617        fragmented_body: None,
618    })
619}
620
621fn recover_malformed_exported_base_class<'tree>(
622    node: Node<'tree>,
623    source: &str,
624) -> Option<RecoveredExportedClass<'tree>> {
625    if node.kind() != "declaration" {
626        return None;
627    }
628    let class_node = node.child_by_field_name("type")?;
629    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
630        return None;
631    }
632    let macro_name = class_node
633        .child_by_field_name("name")
634        .and_then(|name| direct_identifier_name(name, source))?;
635    if !cpp_export_macro_token(&macro_name) {
636        return None;
637    }
638
639    let mut named_cursor = node.walk();
640    let mut named = node.named_children(&mut named_cursor);
641    if named
642        .next()
643        .is_none_or(|child| !same_node(child, class_node))
644    {
645        return None;
646    }
647    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
648    if displaced.kind() != "ERROR" {
649        return None;
650    }
651    let name = displaced_exported_class_name(displaced, source)?;
652
653    let remaining = named.collect::<Vec<_>>();
654    let init = *remaining.last()?;
655    if init.kind() != "init_declarator" {
656        return None;
657    }
658    let final_base = init
659        .child_by_field_name("declarator")
660        .and_then(|base| recovered_malformed_base_name(base, source))?;
661    let body = init.child_by_field_name("value")?;
662    // A complete reduction has a real closing brace here. In Chromium's Widget
663    // declaration, tree-sitter instead emits the same direct `}` slot as a
664    // zero-width missing node where the first body macro truncates the prefix.
665    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
666        return None;
667    }
668
669    if remaining[..remaining.len() - 1]
670        .iter()
671        .any(|child| match child.kind() {
672            "qualified_identifier"
673            | "scoped_type_identifier"
674            | "type_identifier"
675            | "identifier" => false,
676            "ERROR" => !is_malformed_inheritance_access(*child, source),
677            _ => true,
678        })
679    {
680        return None;
681    }
682
683    let mut raw_supertypes = Vec::new();
684    for base in &remaining[..remaining.len() - 1] {
685        if base.kind() == "ERROR" {
686            continue;
687        }
688        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
689    }
690    raw_supertypes.push(final_base);
691
692    Some(RecoveredExportedClass {
693        declaration_node: node,
694        name,
695        body: Some(body),
696        raw_supertypes: Some(raw_supertypes),
697        uses_initializer_body: true,
698        fragmented_body: fragmented_export_body_region(node, body, source),
699    })
700}
701
702/// Locate the true class-body region for a fragmented multiple-base export class.
703///
704/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
705/// emits in place of the real class body. Tree-sitter reduces that body in one of
706/// two shapes, both of which lose the members from the recovered node:
707///
708/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
709///   a real closing brace and holds the whole body text inline. The interior between
710///   the braces reparses to the members directly.
711/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
712///   first member with a zero-width MISSING `}`; every later member -- and the real
713///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
714///   siblings. The interior runs from the opening brace to that displaced `}`.
715///
716/// Returns the interior byte range to reparse plus the full class navigation range.
717fn fragmented_export_body_region(
718    node: Node<'_>,
719    body: Node<'_>,
720    source: &str,
721) -> Option<FragmentedExportBody> {
722    let reparse_start = body.start_byte() + 1;
723    let close = direct_close_brace(body)?;
724    if close.end_byte() > close.start_byte() {
725        return Some(FragmentedExportBody {
726            reparse_start,
727            reparse_end: close.start_byte(),
728            class_range: cpp_declaration_range(node),
729        });
730    }
731    // The closing brace was displaced past the recovered node. A balanced nested
732    // class keeps its own braces, so the first lone-`}` sibling is this class's.
733    let mut sibling = node.next_named_sibling();
734    let displaced_close = loop {
735        let Some(current) = sibling else {
736            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
737        };
738        if cpp_is_stray_close_brace(current, source) {
739            break current;
740        }
741        sibling = current.next_named_sibling();
742    };
743    Some(FragmentedExportBody {
744        reparse_start,
745        reparse_end: displaced_close.start_byte(),
746        class_range: Range {
747            start_byte: node.start_byte(),
748            end_byte: displaced_close.end_byte(),
749            start_line: node.start_position().row + 1,
750            end_line: displaced_close.end_position().row + 1,
751        },
752    })
753}
754
755/// Locate the true class-body region for the export-macro class shape that
756/// tree-sitter promotes to a `function_definition`.
757///
758/// In this shape the synthetic function body closes at the first inline
759/// method, while the class's real members continue as root-level siblings until
760/// a stray `}` followed by the displaced class `;`. Reparse the complete
761/// interior so those siblings are visited with the recovered class scope.
762fn fragmented_export_function_body_region(
763    node: Node<'_>,
764    body: Node<'_>,
765    source: &str,
766    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
767) -> Option<FragmentedExportBody> {
768    let reparse_start = body.start_byte().checked_add(1)?;
769    if let Some(boundary) = displaced_namespace {
770        return Some(FragmentedExportBody {
771            reparse_start,
772            reparse_end: boundary.class_close.start_byte(),
773            class_range: Range {
774                start_byte: node.start_byte(),
775                end_byte: boundary.class_semicolon.end_byte(),
776                start_line: node.start_position().row + 1,
777                end_line: boundary.class_semicolon.end_position().row + 1,
778            },
779        });
780    }
781    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
782    let boundary =
783        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
784    let boundary_index = boundary.and_then(|boundary| {
785        siblings
786            .iter()
787            .position(|candidate| same_node(*candidate, boundary))
788    });
789    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
790    let mut sibling_index = 0;
791    // A complete recovered class's synthetic wrapper is immediately followed
792    // by its displaced semicolon (comments and a trailing attribute macro --
793    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
794    // may sit between the body and that semicolon). Only scan for a later
795    // stray close when real member siblings intervene; otherwise every earlier
796    // complete class would borrow the next malformed class's close and claim
797    // its members. The trailing-attribute case is the gtest shape: the scan
798    // borrowed a close ~1900 lines later and re-owned a following
799    // `namespace testing { namespace internal {` block as class members,
800    // doubling the package path ("testing::internal::testing::internal") and
801    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
802    // boundary assert (#2297).
803    while let Some(current) = siblings.get(sibling_index).copied() {
804        if current.kind() == "comment" {
805            sibling_index += 1;
806            continue;
807        }
808        if is_trailing_attribute_macro_sibling(current) {
809            sibling_index += 1;
810            continue;
811        }
812        if cpp_is_stray_semicolon(current, source) {
813            return None;
814        }
815        break;
816    }
817    while let Some(current) = siblings.get(sibling_index).copied() {
818        let next = siblings.get(sibling_index + 1).copied();
819        if cpp_is_stray_close_brace(current, source)
820            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
821        {
822            let semicolon = next.expect("checked above");
823            return Some(FragmentedExportBody {
824                reparse_start,
825                reparse_end: current.start_byte(),
826                class_range: Range {
827                    start_byte: node.start_byte(),
828                    end_byte: semicolon.end_byte(),
829                    start_line: node.start_position().row + 1,
830                    end_line: semicolon.end_position().row + 1,
831                },
832            });
833        }
834        // When the final access label keeps the class close in its malformed
835        // declaration body, tree-sitter nests the lone `}` ERROR below the
836        // label instead of exposing it as a direct sibling. Search only the
837        // scattered siblings after the synthetic wrapper. The first such
838        // close is the class terminator because nested class bodies retain
839        // their own balanced class_specifier nodes.
840        if current.start_byte() >= body.end_byte()
841            && let Some(close) = cpp_nested_stray_close_brace(current, source)
842        {
843            return Some(FragmentedExportBody {
844                reparse_start,
845                reparse_end: close.start_byte(),
846                class_range: Range {
847                    start_byte: node.start_byte(),
848                    end_byte: current.end_byte(),
849                    start_line: node.start_position().row + 1,
850                    end_line: current.end_position().row + 1,
851                },
852            });
853        }
854        sibling_index += 1;
855    }
856    boundary.map(|boundary| FragmentedExportBody {
857        reparse_start,
858        reparse_end: boundary.start_byte(),
859        class_range: Range {
860            start_byte: node.start_byte(),
861            end_byte: boundary.start_byte(),
862            start_line: node.start_position().row + 1,
863            end_line: boundary.start_position().row + 1,
864        },
865    })
866}
867
868/// Find a later macro-export class that tree-sitter lifted through an enclosing
869/// preprocessor container. A class that is still a direct sibling can be a
870/// nested member of the current fragmented class, so only a changed parent is
871/// a proven boundary between the two recovered class envelopes.
872fn fragmented_export_sibling_class_boundary<'tree>(
873    node: Node<'tree>,
874    source: &str,
875    ancestry: &ParentIndex<'tree>,
876) -> Option<Node<'tree>> {
877    let node_parent = ancestry.parent(node)?;
878    cpp_following_named_siblings(node, source, ancestry)
879        .into_iter()
880        .find(|candidate| {
881            recover_exported_class_function_definition(*candidate, source).is_some()
882                && ancestry
883                    .parent(*candidate)
884                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
885        })
886}
887
888/// A trailing attribute macro after a recovered class's closing brace, spelled
889/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
890/// bare identifier is never a class member (members need a type), so this
891/// sibling can only be the class's own tail (#2297).
892fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
893    if node.kind() != "expression_statement" {
894        return false;
895    }
896    let mut cursor = node.walk();
897    let mut children = node.named_children(&mut cursor);
898    children
899        .next()
900        .is_some_and(|child| child.kind() == "identifier")
901        && children.next().is_none()
902}
903
904/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
905/// export-class wrapper can place the class close inside an access-label node,
906/// so direct-sibling checks alone miss the boundary.  Walk named CST children
907/// only; the helper does not inspect source text beyond the existing structured
908/// stray-brace predicate.
909fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
910    let mut stack = vec![node];
911    while let Some(current) = stack.pop() {
912        if cpp_is_stray_close_brace(current, source) {
913            return Some(current);
914        }
915        let mut cursor = current.walk();
916        stack.extend(current.named_children(&mut cursor));
917    }
918    None
919}
920
921/// Return named siblings that follow `node`, including siblings that tree-sitter
922/// attached to an enclosing container after malformed recovery split the local
923/// declaration list. Stop at the first structurally visible class close so a
924/// later namespace or exported class cannot supply the recovery boundary.
925///
926/// The climb takes a [`ParentIndex`] because it runs once per visited node and
927/// walks every level to the root: with `Node::parent`, which re-descends from
928/// the root on each call, a translation unit holding thousands of siblings --
929/// generated instruction tables, for instance -- made this the whole cost of
930/// indexing the file. Callers inside the declaration walk pass the walk's
931/// index; the four that ask from outside a traversal pass
932/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
933/// behaviour is unchanged.
934fn cpp_following_named_siblings<'tree>(
935    node: Node<'tree>,
936    source: &str,
937    ancestry: &ParentIndex<'tree>,
938) -> Vec<Node<'tree>> {
939    let mut siblings = Vec::new();
940    let mut anchor = node;
941    while let Some(parent) = ancestry.parent(anchor) {
942        let at_translation_unit = parent.kind() == "translation_unit";
943        let mut cursor = parent.walk();
944        assert!(
945            cursor.goto_first_child(),
946            "ParentIndex returned a parent without children"
947        );
948        while !same_node(cursor.node(), anchor) {
949            assert!(
950                cursor.goto_next_sibling(),
951                "ParentIndex parent does not contain its child anchor"
952            );
953        }
954        while cursor.goto_next_sibling() {
955            let current = cursor.node();
956            if !current.is_named() {
957                continue;
958            }
959            if at_translation_unit
960                && (current.kind() == "namespace_definition"
961                    || (current.kind() == "function_definition"
962                        && first_class_like_child(current).is_some()))
963            {
964                return siblings;
965            }
966            siblings.push(current);
967            if cpp_is_stray_close_brace(current, source) {
968                if let Some(semicolon) = current
969                    .next_named_sibling()
970                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
971                {
972                    siblings.push(semicolon);
973                }
974                return siblings;
975            }
976            if current.start_byte() >= node.end_byte()
977                && matches!(current.kind(), "ERROR" | "labeled_statement")
978                && cpp_nested_stray_close_brace(current, source).is_some()
979            {
980                return siblings;
981            }
982        }
983        anchor = parent;
984    }
985    siblings
986}
987
988fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
989    if node.start_byte() >= class_end {
990        return false;
991    }
992    node.end_byte() <= class_end
993        || cpp_nested_stray_close_brace(node, source)
994            .is_some_and(|close| close.start_byte() == class_end)
995}
996
997/// A class whose head tree-sitter kept in one malformed container while the
998/// members after the first and the closing `};` scattered across that
999/// container's following siblings.
1000struct FragmentedClassRecovery<'tree> {
1001    declaration_node: Node<'tree>,
1002    name: String,
1003    raw_supertypes: Vec<String>,
1004    body: FragmentedExportBody,
1005}
1006
1007/// Recover a class whose opening prefix is retained in one ERROR node while one
1008/// or more nested class closes and the outer close are displaced to sibling
1009/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1010/// tokens establish nesting depth and the displaced close nodes terminate it.
1011///
1012/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1013/// `class` keyword as the container's first token. A function-like export macro
1014/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1015/// body-less `class_specifier` named after the macro followed by the
1016/// invocation's `(`, arguments and `)`, with the real class name and the base
1017/// clause left as bare tokens after it (#2924).
1018fn fragmented_class_body<'tree>(
1019    node: Node<'tree>,
1020    source: &str,
1021) -> Option<FragmentedClassRecovery<'tree>> {
1022    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1023        return Some(recovered);
1024    }
1025    let supported_container = node.kind() == "ERROR"
1026        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1027    if !supported_container {
1028        return None;
1029    }
1030    let mut cursor = node.walk();
1031    let children = node.children(&mut cursor).collect::<Vec<_>>();
1032    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1033        return Some(recovered);
1034    }
1035    let keyword = children.first()?;
1036    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1037        return None;
1038    }
1039    let name_node = children
1040        .iter()
1041        .copied()
1042        .skip(1)
1043        .find(|child| child.is_named())?;
1044    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1045        return None;
1046    }
1047    let name = normalize_cpp_whitespace(node_text(name_node, source));
1048    if name.is_empty() || cpp_export_macro_token(&name) {
1049        return None;
1050    }
1051    let open_index = children.iter().position(|child| child.kind() == "{")?;
1052    Some(FragmentedClassRecovery {
1053        declaration_node: node,
1054        name,
1055        raw_supertypes: extract_cpp_supertypes(node, source),
1056        body: fragmented_displaced_class_body(node, &children, open_index, source)?,
1057    })
1058}
1059
1060/// The fragmented body of a function-like export-macro class head that
1061/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1062/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1063/// public virtual EC_PublicKey {`, #2924).
1064///
1065/// The class name is read positionally by [`recovered_export_head_name`] -- the
1066/// last identifier before the head ends at `final`, at the base clause `:`, or
1067/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1068/// spelled with `virtual`, with `final`, or with qualified base names needs no
1069/// arm of its own.
1070fn fragmented_export_macro_class_body<'tree>(
1071    node: Node<'tree>,
1072    children: &[Node<'tree>],
1073    source: &str,
1074) -> Option<FragmentedClassRecovery<'tree>> {
1075    let class_node = *children.first()?;
1076    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1077        return None;
1078    }
1079    // The identifier tree-sitter took for the class name is the export macro, a
1080    // function-like invocation when the `(` of its argument list follows it
1081    // directly. Spelling plays no part (#2557).
1082    class_node
1083        .child_by_field_name("name")
1084        .and_then(|name| direct_identifier_name(name, source))?;
1085    let invocation = *children.get(1)?;
1086    if invocation.is_named() || invocation.kind() != "(" {
1087        return None;
1088    }
1089    let open_index = children
1090        .iter()
1091        .position(|child| !child.is_named() && child.kind() == "{")?;
1092    let open = children[open_index];
1093    let name_node = recovered_export_head_name(node, open, source)?;
1094    let name = normalize_cpp_whitespace(node_text(name_node, source));
1095    if name.is_empty() || cpp_export_macro_token(&name) {
1096        return None;
1097    }
1098    Some(FragmentedClassRecovery {
1099        declaration_node: node,
1100        name,
1101        raw_supertypes: recovered_export_head_bases(
1102            node,
1103            name_node.end_byte(),
1104            open.start_byte(),
1105            source,
1106        ),
1107        body: fragmented_displaced_class_body(node, children, open_index, source)
1108            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1109    })
1110}
1111
1112/// The class body a fragmented head opened at `children[open_index]` but could
1113/// not close: tree-sitter scattered the remaining members and the closing `};`
1114/// across the container's following siblings. The direct class tokens after the
1115/// opening brace say how many closes the nested classes consume before this
1116/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1117fn fragmented_displaced_class_body(
1118    node: Node<'_>,
1119    children: &[Node<'_>],
1120    open_index: usize,
1121    source: &str,
1122) -> Option<FragmentedExportBody> {
1123    let open = children[open_index];
1124    let nested_class_opens = children[open_index + 1..]
1125        .iter()
1126        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1127        .count();
1128    let mut closes_remaining = 1 + nested_class_opens;
1129    let mut sibling = node.next_named_sibling();
1130    while let Some(candidate) = sibling {
1131        let next = candidate.next_named_sibling();
1132        if cpp_is_stray_close_brace(candidate, source) {
1133            closes_remaining -= 1;
1134            if closes_remaining == 0 {
1135                let semicolon = next.filter(|node| cpp_is_stray_semicolon(*node, source))?;
1136                if open.end_byte() >= candidate.start_byte() {
1137                    return None;
1138                }
1139                return Some(FragmentedExportBody {
1140                    reparse_start: open.end_byte(),
1141                    reparse_end: candidate.start_byte(),
1142                    class_range: Range {
1143                        start_byte: node.start_byte(),
1144                        end_byte: semicolon.end_byte(),
1145                        start_line: node.start_position().row + 1,
1146                        end_line: semicolon.end_position().row + 1,
1147                    },
1148                });
1149            }
1150        }
1151        sibling = next;
1152    }
1153    None
1154}
1155
1156/// The body of a fragmented export-macro class head whose close tree-sitter
1157/// spent on the enclosing container instead: Botan's
1158/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1159/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1160/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1161/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1162/// (#2924).
1163fn fragmented_container_close_class_body(
1164    node: Node<'_>,
1165    open: Node<'_>,
1166    source: &str,
1167) -> Option<FragmentedExportBody> {
1168    let parent = node.parent()?;
1169    if !matches!(
1170        parent.kind(),
1171        "declaration_list" | "field_declaration_list" | "compound_statement"
1172    ) {
1173        return None;
1174    }
1175    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1176    // The container's close is this class's close only when the body's own
1177    // brace balance says so. Without that cross-check -- the one issue #1524
1178    // already uses on a mis-closed wrapper body -- a class that really does end
1179    // earlier would claim every later container-level declaration as a member.
1180    if cpp_matching_close_brace(source, open.start_byte()) != Some(close.start_byte())
1181        || open.end_byte() >= close.start_byte()
1182    {
1183        return None;
1184    }
1185    Some(FragmentedExportBody {
1186        reparse_start: open.end_byte(),
1187        reparse_end: close.start_byte(),
1188        class_range: Range {
1189            start_byte: node.start_byte(),
1190            end_byte: close.end_byte(),
1191            start_line: node.start_position().row + 1,
1192            end_line: close.end_position().row + 1,
1193        },
1194    })
1195}
1196
1197pub(crate) fn recovered_fragmented_class_has_body(
1198    node: Node<'_>,
1199    source: &str,
1200    expected_name: &str,
1201    expected_range: &Range,
1202) -> bool {
1203    fragmented_class_body(node, source).is_some_and(|recovered| {
1204        recovered.name == expected_name
1205            && recovered.body.class_range.start_byte == expected_range.start_byte
1206            && recovered.body.class_range.end_byte == expected_range.end_byte
1207    })
1208}
1209
1210/// Recover a plain class whose parser-visible body ends inside a malformed
1211/// inline member. Tree-sitter then attaches either the next real member
1212/// declarator or the unfinished `else` branch directly to the outer function
1213/// definition and leaves the class's actual `};` among later siblings. Those
1214/// structured continuations and the close/semicolon siblings establish the
1215/// complete body envelope without interpreting source text.
1216fn fragmented_plain_class_declaration_body<'tree>(
1217    node: Node<'tree>,
1218    source: &str,
1219) -> Option<FragmentedClassRecovery<'tree>> {
1220    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1221        return None;
1222    }
1223    let class_node = node.child_by_field_name("type")?;
1224    if !matches!(
1225        class_node.kind(),
1226        "class_specifier" | "struct_specifier" | "union_specifier"
1227    ) {
1228        return None;
1229    }
1230    let name_node = class_node.child_by_field_name("name")?;
1231    let name = normalize_cpp_whitespace(node_text(name_node, source));
1232    if name.is_empty() || cpp_export_macro_token(&name) {
1233        return None;
1234    }
1235    let body = cpp_body_node(class_node)?;
1236    if body.kind() != "field_declaration_list" {
1237        return None;
1238    }
1239    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1240        if declarator.start_byte() < class_node.end_byte() {
1241            return None;
1242        }
1243        let mut cursor = node.walk();
1244        node.named_children(&mut cursor).any(|child| {
1245            if child.kind() != "ERROR"
1246                || child.start_byte() < class_node.end_byte()
1247                || child.end_byte() > declarator.start_byte()
1248            {
1249                return false;
1250            }
1251            let mut cursor = child.walk();
1252            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1253            let Some((return_type, attributes)) = components.split_last() else {
1254                return false;
1255            };
1256            matches!(
1257                return_type.kind(),
1258                "identifier"
1259                    | "type_identifier"
1260                    | "primitive_type"
1261                    | "decltype"
1262                    | "placeholder_type_specifier"
1263            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1264                && attributes.iter().all(|attribute| {
1265                    matches!(attribute.kind(), "identifier" | "type_identifier")
1266                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1267                            *attribute, source,
1268                        )))
1269                })
1270        })
1271    } else {
1272        let mut cursor = node.walk();
1273        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1274        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1275            if same_node(*candidate_class, class_node)
1276                && continuation.kind() == "identifier"
1277                && node_text(*continuation, source) == "else"
1278                && continuation_body.kind() == "compound_statement"
1279                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1280                && continuation_body
1281                    .child(continuation_body.child_count().saturating_sub(1))
1282                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1283    };
1284    if !displaced_member {
1285        return None;
1286    }
1287    let open = body
1288        .children(&mut body.walk())
1289        .find(|child| child.kind() == "{")?;
1290    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1291    let ordinary_boundary =
1292        siblings
1293            .iter()
1294            .copied()
1295            .enumerate()
1296            .find_map(|(close_index, close)| {
1297                cpp_is_stray_close_brace(close, source)
1298                    .then(|| {
1299                        siblings
1300                            .get(close_index + 1)
1301                            .copied()
1302                            .filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source))
1303                            .map(|semicolon| (close, semicolon))
1304                    })
1305                    .flatten()
1306            });
1307    let (close, semicolon) =
1308        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1309            (boundary.class_close, boundary.class_semicolon)
1310        } else {
1311            ordinary_boundary?
1312        };
1313    if open.end_byte() >= close.start_byte() {
1314        return None;
1315    }
1316    Some(FragmentedClassRecovery {
1317        declaration_node: class_node,
1318        name,
1319        raw_supertypes: extract_cpp_supertypes(class_node, source),
1320        body: FragmentedExportBody {
1321            reparse_start: open.end_byte(),
1322            reparse_end: close.start_byte(),
1323            class_range: Range {
1324                start_byte: class_node.start_byte(),
1325                end_byte: semicolon.end_byte(),
1326                start_line: class_node.start_position().row + 1,
1327                end_line: semicolon.end_position().row + 1,
1328            },
1329        },
1330    })
1331}
1332
1333fn displaced_export_function_namespace_shape<'tree>(
1334    declaration: Node<'tree>,
1335    source: &str,
1336) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1337    let mut nested = Vec::new();
1338    for index in (0..declaration.named_child_count()).rev() {
1339        nested.push(declaration.named_child(index)?);
1340    }
1341    while let Some(current) = nested.pop() {
1342        // A recovered export class nested in this class can consume the first
1343        // parser-visible namespace close itself. In that shape the existing
1344        // later-class boundary logic already distinguishes the nested and
1345        // namespace-sibling owners; do not mistake the nested close for this
1346        // class's terminator.
1347        if recover_exported_class_function_definition(current, source).is_some() {
1348            return None;
1349        }
1350        for index in (0..current.named_child_count()).rev() {
1351            nested.push(current.named_child(index)?);
1352        }
1353    }
1354    let mut same_envelope_sibling = declaration.next_named_sibling();
1355    while let Some(current) = same_envelope_sibling {
1356        if recover_exported_class_function_definition(current, source).is_some() {
1357            return None;
1358        }
1359        same_envelope_sibling = current.next_named_sibling();
1360    }
1361    let declaration_list = declaration.parent()?;
1362    if declaration_list.kind() != "declaration_list" {
1363        return None;
1364    }
1365    let namespace = declaration_list.parent()?;
1366    if namespace.kind() != "namespace_definition"
1367        || namespace.child_by_field_name("body") != Some(declaration_list)
1368    {
1369        return None;
1370    }
1371    let class_close = direct_close_brace(declaration_list)?;
1372    let trailing_semicolon = namespace.next_named_sibling()?;
1373    if trailing_semicolon.kind() != "expression_statement"
1374        || trailing_semicolon.named_child_count() != 0
1375    {
1376        return None;
1377    }
1378    // A chain of malformed export classes can consume one parser-visible
1379    // namespace close per class. Walk through the enclosing sibling levels so
1380    // the later real namespace close remains the structural boundary; a
1381    // direct next-sibling walk stops at the first collapsed namespace and
1382    // incorrectly makes its intervening items members of this class.
1383    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1384    let trailing_index = siblings
1385        .iter()
1386        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1387    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1388        recover_exported_class_function_definition(*candidate, source).is_some()
1389    }) {
1390        // Consecutive recovered classes already have an exact sibling-class
1391        // boundary. Preserve that established path, including nested export
1392        // classes, instead of interpreting the first class close as a
1393        // collapsed namespace boundary.
1394        return None;
1395    }
1396    let mut namespace_items = Vec::new();
1397    let mut nested_fragment_end = 0;
1398    for current in siblings.into_iter().skip(trailing_index + 1) {
1399        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1400        {
1401            return Some(DisplacedFragmentNamespaceBoundary {
1402                class_close,
1403                class_semicolon: trailing_semicolon,
1404                namespace_items,
1405            });
1406        }
1407        if current.start_byte() >= nested_fragment_end
1408            && let Some(recovered) = fragmented_class_body(current, source)
1409        {
1410            nested_fragment_end = recovered.body.class_range.end_byte;
1411        } else if current.start_byte() >= nested_fragment_end
1412            && recover_exported_class_function_definition(current, source).is_some()
1413            && let Some(body) = cpp_body_node(current)
1414            && let Some(fragmented) =
1415                fragmented_export_function_body_region(current, body, source, None)
1416        {
1417            nested_fragment_end = fragmented.class_range.end_byte;
1418        }
1419        namespace_items.push(current);
1420    }
1421    None
1422}
1423
1424fn displaced_fragment_namespace_boundary<'tree>(
1425    declaration: Node<'tree>,
1426    body: Node<'tree>,
1427    source: &str,
1428) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1429    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1430    let reparse_start = body.start_byte() + 1;
1431    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1432    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1433}
1434
1435/// Recover the class/namespace brace geometry for a declaration whose class
1436/// close tree-sitter consumed as the enclosing namespace close. This proof is
1437/// independent of whether every member in the class body can be reparsed: the
1438/// ordinary-tree fallback can still re-own bounded sibling declarations when
1439/// an unknown macro makes the complete body reparse unsafe.
1440fn displaced_fragment_namespace_geometry<'tree>(
1441    declaration: Node<'tree>,
1442    source: &str,
1443) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1444    // A templated class's malformed function wrapper remains beneath the
1445    // template node even though its later members have escaped to the
1446    // enclosing declaration list. Lift only that exact declaration child.
1447    let envelope = declaration
1448        .parent()
1449        .filter(|parent| {
1450            parent.kind() == "template_declaration"
1451                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1452        })
1453        .unwrap_or(declaration);
1454    let declaration_list = envelope.parent()?;
1455    if declaration_list.kind() != "declaration_list" {
1456        return None;
1457    }
1458    let namespace = declaration_list.parent()?;
1459    if namespace.kind() != "namespace_definition"
1460        || namespace.child_by_field_name("body") != Some(declaration_list)
1461    {
1462        return None;
1463    }
1464    let class_close = direct_close_brace(declaration_list)?;
1465    let trailing_semicolon = namespace.next_named_sibling()?;
1466    if trailing_semicolon.kind() != "expression_statement"
1467        || trailing_semicolon.named_child_count() != 0
1468    {
1469        return None;
1470    }
1471    let mut namespace_items = Vec::new();
1472    let mut sibling = trailing_semicolon.next_named_sibling();
1473    let mut nested_fragment_end = 0;
1474    loop {
1475        let current = sibling?;
1476        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1477        {
1478            break;
1479        }
1480        if current.start_byte() >= nested_fragment_end
1481            && let Some(recovered) = fragmented_class_body(current, source)
1482        {
1483            nested_fragment_end = recovered.body.class_range.end_byte;
1484        }
1485        namespace_items.push(current);
1486        sibling = current.next_named_sibling();
1487    }
1488    Some(DisplacedFragmentNamespaceBoundary {
1489        class_close,
1490        class_semicolon: trailing_semicolon,
1491        namespace_items,
1492    })
1493}
1494
1495/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1496fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1497    (0..node.child_count())
1498        .filter_map(|index| node.child(index))
1499        .find(|child| !child.is_named() && child.kind() == "}")
1500}
1501
1502/// A displaced lone closing brace: the class close that the fragmented multiple-base
1503/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1504fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1505    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1506}
1507
1508/// Byte offset of the `}` matching the `{` at `open_byte`, scanning the source
1509/// text while skipping line/block comments and string/char literals. The
1510/// exported-class recovery needs this when tree-sitter's bogus
1511/// `function_definition` body runs past the class's true closing brace and
1512/// swallows following siblings (issue #1524): the grammar tree carries no
1513/// usable close node (the body ends in a zero-width `MISSING "}"`), so the
1514/// close is located textually. Returns `None` when the text is unbalanced or
1515/// contains a construct the scanner deliberately does not interpret (raw
1516/// strings) -- callers treat that as "cannot partition" and keep the
1517/// un-split recovery.
1518fn cpp_matching_close_brace(source: &str, open_byte: usize) -> Option<usize> {
1519    let bytes = source.as_bytes();
1520    if bytes.get(open_byte) != Some(&b'{') {
1521        return None;
1522    }
1523    let mut depth = 0usize;
1524    let mut i = open_byte;
1525    while i < bytes.len() {
1526        match bytes[i] {
1527            b'{' => depth += 1,
1528            b'}' => {
1529                depth = depth.checked_sub(1)?;
1530                if depth == 0 {
1531                    return Some(i);
1532                }
1533            }
1534            b'/' if bytes.get(i + 1) == Some(&b'/') => {
1535                while i < bytes.len() && bytes[i] != b'\n' {
1536                    i += 1;
1537                }
1538                continue;
1539            }
1540            b'/' if bytes.get(i + 1) == Some(&b'*') => {
1541                i += 2;
1542                while i + 1 < bytes.len() && !(bytes[i] == b'*' && bytes[i + 1] == b'/') {
1543                    i += 1;
1544                }
1545                i = i.checked_add(2).filter(|&end| end <= bytes.len())?;
1546                continue;
1547            }
1548            quote @ (b'"' | b'\'') => {
1549                // Raw strings (R"(...)") can hold unescaped quotes and braces;
1550                // bail out rather than mis-count.
1551                if quote == b'"' && i > 0 && bytes[i - 1] == b'R' {
1552                    return None;
1553                }
1554                i += 1;
1555                while i < bytes.len() && bytes[i] != quote {
1556                    i += if bytes[i] == b'\\' { 2 } else { 1 };
1557                }
1558                if i >= bytes.len() {
1559                    return None;
1560                }
1561            }
1562            _ => {}
1563        }
1564        i += 1;
1565    }
1566    None
1567}
1568
1569fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1570    let mut name = None;
1571    let mut colon_count = 0;
1572    let mut access_count = 0;
1573    for index in 0..node.child_count() {
1574        let child = node.child(index)?;
1575        match child.kind() {
1576            "identifier" | "type_identifier" if child.is_named() => {
1577                if name.is_some() {
1578                    return None;
1579                }
1580                let candidate = normalize_cpp_whitespace(node_text(child, source));
1581                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1582                    return None;
1583                }
1584                name = Some(candidate);
1585            }
1586            "template_function" | "template_type" if child.is_named() => {
1587                if name.is_some() {
1588                    return None;
1589                }
1590                let candidate = child
1591                    .child_by_field_name("name")
1592                    .and_then(|name| direct_identifier_name(name, source))?;
1593                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1594                    return None;
1595                }
1596                name = Some(candidate);
1597            }
1598            ":" if !child.is_named() => colon_count += 1,
1599            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1600            _ => return None,
1601        }
1602    }
1603    (colon_count == 1 && access_count == 1)
1604        .then_some(name)
1605        .flatten()
1606}
1607
1608fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1609    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1610        return false;
1611    }
1612    node.named_child(0)
1613        .and_then(|child| direct_identifier_name(child, source))
1614        .is_some_and(|name| matches!(name.as_str(), "public" | "protected" | "private"))
1615}
1616
1617fn has_direct_token(node: Node<'_>, expected_kind: &str) -> bool {
1618    (0..node.child_count()).any(|index| {
1619        node.child(index)
1620            .is_some_and(|child| !child.is_named() && child.kind() == expected_kind)
1621    })
1622}
1623
1624fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1625    match node.kind() {
1626        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1627            recovered_base_atom(node, source)
1628        }
1629        "template_type" | "template_function" => node
1630            .child_by_field_name("name")
1631            .and_then(|name| recovered_malformed_base_name(name, source)),
1632        "ERROR" => None,
1633        "qualified_identifier" | "scoped_type_identifier" => {
1634            let suffix = node
1635                .child_by_field_name("name")
1636                .and_then(|name| recovered_malformed_base_name(name, source))?;
1637            let scope = node
1638                .child_by_field_name("scope")
1639                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1640            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1641                malformed_qualified_prefix(node, source)?
1642            } else {
1643                if malformed_qualified_prefix(node, source).is_some() {
1644                    return None;
1645                }
1646                scope
1647            };
1648            Some(format!("{prefix}::{suffix}"))
1649        }
1650        _ => None,
1651    }
1652}
1653
1654fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1655    if !matches!(
1656        node.kind(),
1657        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1658    ) {
1659        return None;
1660    }
1661    let name = normalize_cpp_whitespace(node_text(node, source));
1662    (!name.is_empty()).then_some(name)
1663}
1664
1665fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1666    let mut prefix = None;
1667    let mut cursor = node.walk();
1668    for error in node
1669        .named_children(&mut cursor)
1670        .filter(|child| child.kind() == "ERROR")
1671    {
1672        if prefix.is_some() {
1673            return None;
1674        }
1675        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1676        // the qualified name's scope and leaves `virtual` beside the real
1677        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1678        // component of the base's name (#2924).
1679        let mut error_cursor = error.walk();
1680        let atoms = error
1681            .named_children(&mut error_cursor)
1682            .map(|child| recovered_base_atom(child, source))
1683            .collect::<Option<Vec<_>>>()?;
1684        let [atom] = atoms
1685            .iter()
1686            .filter(|atom| atom.as_str() != "virtual")
1687            .collect::<Vec<_>>()[..]
1688        else {
1689            return None;
1690        };
1691        prefix = Some(atom.clone());
1692    }
1693    prefix
1694}
1695
1696/// One declaration an attribute-like macro invocation swallowed into a
1697/// declaration-scope `ERROR`, with the byte range that spells it.
1698/// What [`stranded_declaration_run`] read out of one node.
1699struct StrandedRun<'tree> {
1700    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1701    /// Whether every part of the node read as part of a declaration. False when
1702    /// a part the reader does not understand ended it early, or when the last
1703    /// parts were types with no declarator after them. Callers that index what
1704    /// was found keep the declarations either way; a caller deciding whether a
1705    /// whole region is safe to index requires this.
1706    complete: bool,
1707}
1708
1709struct MacroWrappedDeclaration<'tree> {
1710    declarator: Node<'tree>,
1711    range: Range,
1712    /// Whether the recovered declaration spells `static`. The envelope hides
1713    /// that keyword from the ordinary linkage reader, which looks for it among
1714    /// a declaration node's own children, and internal linkage is what decides
1715    /// whether a header declaration and a body in another file are one symbol.
1716    is_static: bool,
1717}
1718
1719/// Whether `node` stands where declarations live: directly in the translation
1720/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1721/// stands in one of those.
1722///
1723/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1724/// where it stands. The case that matters is a whole translation unit the
1725/// parser could not close: whisper.cpp's `include/whisper.h` opens
1726/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1727/// so the braces never balance in the token stream tree-sitter sees, the file's
1728/// own root node is an `ERROR`, and every top-level declaration in the file is
1729/// a child of it (#3094). Without this the macro recoveries below read nothing
1730/// in such a file, because none of their envelopes is in a declaration scope
1731/// the parser managed to build.
1732fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1733    declaration_scope_container(node, ancestry).is_some()
1734}
1735
1736/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
1737/// end is where a parse failure that starts at `node` can still be doing
1738/// damage: a declaration scope has no synchronization point of its own, so the
1739/// parser carries such a failure to the scope's close.
1740fn declaration_scope_container<'tree>(
1741    node: Node<'tree>,
1742    ancestry: &ParentIndex<'tree>,
1743) -> Option<Node<'tree>> {
1744    let mut parent = ancestry.parent(node)?;
1745    loop {
1746        match parent.kind() {
1747            "translation_unit" => return Some(parent),
1748            "declaration_list" => {
1749                return ancestry
1750                    .parent(parent)
1751                    .is_some_and(|grandparent| {
1752                        matches!(
1753                            grandparent.kind(),
1754                            "namespace_definition" | "linkage_specification"
1755                        )
1756                    })
1757                    .then_some(parent);
1758            }
1759            "ERROR" => match ancestry.parent(parent) {
1760                Some(grandparent) => parent = grandparent,
1761                // A root `ERROR` is the translation unit the parser could not
1762                // build, so its children stand at file scope.
1763                None => return Some(parent),
1764            },
1765            _ => return None,
1766        }
1767    }
1768}
1769
1770/// Whether `node` is an `ERROR` the parser produced where declarations live.
1771fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
1772    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
1773}
1774
1775/// Whether `node` can only be part of what precedes a declarator -- a type, a
1776/// specifier, or a word of the attribute macro's own text that the lexer left
1777/// as a bare identifier -- so a run of these followed by a declarator is one
1778/// declaration the parser failed to group.
1779fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
1780    matches!(
1781        node.kind(),
1782        "identifier"
1783            | "type_identifier"
1784            | "primitive_type"
1785            | "sized_type_specifier"
1786            | "struct_specifier"
1787            | "union_specifier"
1788            | "enum_specifier"
1789            | "type_qualifier"
1790            | "storage_class_specifier"
1791            | "explicit_function_specifier"
1792            | "virtual_function_specifier"
1793            | "qualified_identifier"
1794            | "template_type"
1795            | "dependent_type"
1796            | "placeholder_type_specifier"
1797    )
1798}
1799
1800/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
1801/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
1802/// words the lexer reports as bare identifiers. Anything else in such an
1803/// `ERROR` stops the recovery, so a macro argument that carries structure this
1804/// recovery does not understand is never guessed at.
1805fn is_macro_argument_error(node: Node<'_>) -> bool {
1806    if node.kind() != "ERROR" {
1807        return false;
1808    }
1809    let mut cursor = node.walk();
1810    node.named_children(&mut cursor).all(|child| {
1811        matches!(
1812            child.kind(),
1813            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
1814        )
1815    })
1816}
1817
1818/// The end of a recovered declaration: the declarator's end, extended across
1819/// the `;` the grammar left beside it inside the envelope.
1820///
1821/// The envelope's own end is the hard boundary. The parser hands the last
1822/// declaration's `;` to the sibling statement it recovered with, outside the
1823/// envelope, and a range that reached it would no longer lie inside one node --
1824/// which is how every reader (including the resolver's climb from a range to
1825/// the node that declares it) finds a recovered declaration again.
1826fn recovered_declaration_end(declarator: Node<'_>) -> usize {
1827    declarator
1828        .next_sibling()
1829        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
1830        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
1831}
1832
1833/// The declarations `node` holds after an attribute-like macro cost the parser
1834/// their grouping, in source order.
1835///
1836/// The parser packs the parts into whichever slots it has left. In whisper's
1837/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
1838/// field takes the export macro, the real return type and the *next*
1839/// declaration's declarator both end up inside one sibling `ERROR`, and the
1840/// `declarator` field takes whatever declaration the recovery reached last. In
1841/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
1842/// arrive as bare identifiers and the attributed member and the member after it
1843/// share one declarator node.
1844///
1845/// Both are the same failure, so both get the same reading: flatten the node's
1846/// parts -- splicing each nested `ERROR`'s own children in place, since an
1847/// `ERROR` here is only a grouping failure -- and read the flat run as what it
1848/// spells, a run of type-and-specifier tokens followed by a declarator, over
1849/// and over.
1850///
1851/// Fails closed. A part that is neither a declarator nor something that can
1852/// only precede one ends the recovery there, and the declarations before it are
1853/// kept.
1854fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
1855    let mut parts = Vec::new();
1856    let mut cursor = node.walk();
1857    for child in node.named_children(&mut cursor) {
1858        if child.kind() == "ERROR" {
1859            let mut error_cursor = child.walk();
1860            parts.extend(child.named_children(&mut error_cursor));
1861        } else {
1862            parts.push(child);
1863        }
1864    }
1865
1866    let mut declarations = Vec::new();
1867    let mut start = None;
1868    let mut is_static = false;
1869    let mut complete = true;
1870    for part in parts {
1871        if part.kind() == "comment" {
1872            continue;
1873        }
1874        if let Some(declarator) = extract_function_declarator(part) {
1875            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
1876            declarations.push(MacroWrappedDeclaration {
1877                declarator,
1878                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
1879                is_static,
1880            });
1881            is_static = false;
1882            continue;
1883        }
1884        if !is_recovered_declaration_type_part(part) {
1885            complete = false;
1886            break;
1887        }
1888        is_static |= part.kind() == "storage_class_specifier"
1889            && normalize_cpp_whitespace(node_text(part, source)) == "static";
1890        start.get_or_insert(part.start_byte());
1891    }
1892    StrandedRun {
1893        declarations,
1894        complete: complete && start.is_none(),
1895    }
1896}
1897
1898/// The declarations an attribute-like macro invocation swallowed into a
1899/// declaration-scope `ERROR`, in source order.
1900///
1901/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
1902/// declaration in a macro call:
1903///
1904/// ```text
1905/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
1906///                  struct llama_model * model,
1907///           struct llama_context_params   params),
1908///         "use llama_init_from_model instead");
1909/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
1910/// ```
1911///
1912/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
1913/// holding the macro name, the wrapped declaration as a
1914/// `parameter_declaration`, the hint string as another `ERROR`, and then every
1915/// following declaration as a further `parameter_declaration` until it
1916/// recovers. That is what removed `llama_tokenize` from the index and left the
1917/// seven-argument call in `talk-llama.cpp` with only that file's own
1918/// three-parameter `static` overload to choose from (#2552, and the
1919/// `LLAMA_API` half of #2551).
1920///
1921/// Every part of every swallowed declaration is still a real node; only their
1922/// grouping is lost. This rebuilds the grouping and reads the nodes.
1923fn macro_wrapped_declarations<'tree>(
1924    envelope: Node<'tree>,
1925    source: &str,
1926    ancestry: &ParentIndex<'tree>,
1927) -> Vec<MacroWrappedDeclaration<'tree>> {
1928    let mut declarations = Vec::new();
1929    if !is_declaration_scope_error(envelope, ancestry) {
1930        return declarations;
1931    }
1932    let mut cursor = envelope.walk();
1933    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
1934    let [macro_name, arguments @ ..] = children.as_slice() else {
1935        return declarations;
1936    };
1937    if macro_name.kind() != "identifier" {
1938        return declarations;
1939    }
1940    let mut wrapped_declaration_seen = false;
1941    for argument in arguments {
1942        match argument.kind() {
1943            "comment" => {}
1944            "parameter_declaration" => {
1945                let recovered = stranded_declaration_run(*argument, source).declarations;
1946                if recovered.is_empty() {
1947                    break;
1948                }
1949                wrapped_declaration_seen = true;
1950                declarations.extend(recovered);
1951            }
1952            // The hint string, and only that: an argument the recovery cannot
1953            // read as a declaration is admitted before the wrapped declaration
1954            // is found, so a macro whose first argument is not a declaration
1955            // recovers nothing.
1956            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(*argument) => {}
1957            _ => break,
1958        }
1959    }
1960    declarations
1961}
1962
1963/// What one macro invocation swallowed when it collapsed a whole run of
1964/// declarations.
1965struct CollapsedMacroDeclarationRun {
1966    /// The byte just past the `;` that closes the invocation, which is where
1967    /// the declarations it swallowed begin.
1968    invocation_end: usize,
1969    /// The byte this recovery owns to: the close of the enclosing declaration
1970    /// scope when the invocation swallowed the declarations written after it,
1971    /// and the invocation's own end when the parser instead spread the
1972    /// invocation itself across the container's children and swallowed nothing.
1973    region_end: usize,
1974}
1975
1976/// The tokens one collapsed macro invocation spans, in source order: the leaves
1977/// of `node`, then the leaves of the siblings written after it. Comments and
1978/// the parser's own MISSING tokens are skipped; neither is in the source the
1979/// invocation is written in.
1980///
1981/// The parser puts the parts of a failed invocation wherever it has room. It
1982/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
1983/// `expression_statement` it recovered with; it spreads them flat over the
1984/// container's children; it packs the tail of the file into one of the
1985/// arguments. The grouping therefore says nothing, but the token order still
1986/// spells the invocation, which is what this reads.
1987struct MacroInvocationTokens<'tree> {
1988    stack: Vec<Node<'tree>>,
1989    /// The last sibling handed to `stack`, and the point the next one is asked
1990    /// for. Held as the node rather than as its already-computed successor so
1991    /// the successor is asked for only when the walk actually runs off the end
1992    /// of what it has: `Node::next_sibling` recovers the parent first, which
1993    /// re-descends from the root, so it costs the node's position in the tree.
1994    /// Nearly every node this iterator is built for is rejected on its first
1995    /// token, before any sibling is needed, and computing one eagerly made that
1996    /// rejection cost a walk of the whole container (capstone's generated
1997    /// instruction tables, t19).
1998    frontier: Option<Node<'tree>>,
1999}
2000
2001impl<'tree> MacroInvocationTokens<'tree> {
2002    fn new(node: Node<'tree>) -> Self {
2003        Self {
2004            stack: vec![node],
2005            frontier: Some(node),
2006        }
2007    }
2008}
2009
2010impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2011    type Item = Node<'tree>;
2012
2013    fn next(&mut self) -> Option<Node<'tree>> {
2014        loop {
2015            let Some(node) = self.stack.pop() else {
2016                let sibling = self.frontier?.next_sibling()?;
2017                self.frontier = Some(sibling);
2018                self.stack.push(sibling);
2019                continue;
2020            };
2021            if node.child_count() == 0 {
2022                if node.kind() == "comment" || node.is_missing() {
2023                    continue;
2024                }
2025                return Some(node);
2026            }
2027            let mut cursor = node.walk();
2028            let children = node.children(&mut cursor).collect::<Vec<_>>();
2029            self.stack.extend(children.into_iter().rev());
2030        }
2031    }
2032}
2033
2034/// The macro invocation that begins at `node` when the parser could not group
2035/// it, or `None` when `node` does not begin one.
2036///
2037/// whisper.cpp's bundled `llama.h` writes
2038///
2039/// ```text
2040/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2041///                          const char * path_model,
2042///           struct llama_model_params   params),
2043///         "use llama_model_load_from_file instead");
2044/// ```
2045///
2046/// and its own `include/whisper.h` writes
2047///
2048/// ```text
2049/// WHISPER_DEPRECATED(
2050///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2051///     "use whisper_init_from_file_with_params instead"
2052/// );
2053/// ```
2054///
2055/// tree-sitter cannot know either name is a macro, so it reads the name and its
2056/// `(` as a function declarator whose close it never finds, and carries that
2057/// failure forward over the declarations written after it. In `llama.h` that
2058/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2059/// (#2551). In `whisper.h` it instead flattens the invocations over the
2060/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2061/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2062/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2063/// `function_definition` when a brace block falls in a swallowed tail, because
2064/// the parser borrows it for the body the bogus definition needs, and an
2065/// `ERROR` or a bare token when none does. None of that says anything about the
2066/// construct, so the node's kind is not the criterion; the token order is.
2067///
2068/// The invocation ends at the `)` that closes its own `(`, immediately followed
2069/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2070/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2071/// its own, so a `;` reached before the close means this is some other
2072/// construct -- a macro-defined function body, say -- and the ordinary readers
2073/// keep the node.
2074///
2075/// Fails closed everywhere else too. The head must be an export-macro-shaped
2076/// token followed by `(`, the node must stand where declarations live, and an
2077/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2078/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2079/// nothing past it -- is left to that reader.
2080fn collapsed_macro_declaration_run<'tree>(
2081    node: Node<'tree>,
2082    source: &str,
2083    ancestry: &ParentIndex<'tree>,
2084) -> Option<CollapsedMacroDeclarationRun> {
2085    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2086    let mut tokens = MacroInvocationTokens::new(node);
2087    let name = tokens.next()?;
2088    if !matches!(name.kind(), "identifier" | "type_identifier")
2089        || !cpp_export_macro_token(node_text(name, source))
2090    {
2091        return None;
2092    }
2093    if tokens.next()?.kind() != "(" {
2094        return None;
2095    }
2096    let mut depth = 1usize;
2097    let invocation_end = loop {
2098        let token = tokens.next()?;
2099        match token.kind() {
2100            "(" => depth += 1,
2101            ";" => return None,
2102            ")" => {
2103                depth -= 1;
2104                if depth == 0 {
2105                    let semicolon = tokens.next()?;
2106                    if semicolon.kind() != ";" {
2107                        return None;
2108                    }
2109                    break semicolon.end_byte();
2110                }
2111            }
2112            _ => {}
2113        }
2114    };
2115    // An invocation that reaches the end of its own declaration scope swallowed
2116    // nothing: there is nothing after it left to recover, and the ordinary
2117    // readers have the invocation itself. This is also what stops the scan
2118    // below from re-entering itself, since the region it reparses for one
2119    // invocation ends exactly at that invocation's `;`.
2120    if invocation_end >= container_end {
2121        return None;
2122    }
2123    // Where the damage ends. A node that reaches past its own invocation packed
2124    // real declarations into the invocation's argument list, so the parser was
2125    // still failing when it got there and everything to the close of the scope
2126    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2127    // line 455 with it, and every declaration from there to the end of the file
2128    // is shredded. A node that stops at or before the invocation's `;` holds
2129    // nothing but the invocation, whose own parts the parser spread over the
2130    // siblings after it.
2131    let region_end = if node.end_byte() > invocation_end {
2132        container_end
2133    } else {
2134        invocation_end
2135    };
2136    Some(CollapsedMacroDeclarationRun {
2137        invocation_end,
2138        region_end,
2139    })
2140}
2141
2142/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2143/// ordinary class body, and the members it swallowed.
2144///
2145/// Botan deprecates members that way:
2146///
2147/// ```text
2148/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2149/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2150/// ```
2151///
2152/// The parser reads the macro name as the member's type and its argument list
2153/// as a parenthesized declarator, which then swallows the attributed member
2154/// *and* the member written after it: a `field_declaration` whose `type` is a
2155/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2156/// opening with an `ERROR` whose first part is a bare identifier -- the first
2157/// word of the string, which the lexer could not keep together.
2158///
2159/// The macro's spelling is not the criterion; that structural shape is. The
2160/// returned declarations are in source order, the first being the attributed
2161/// member and the rest the members the declarator swallowed after it.
2162fn string_attribute_macro_member_declarators<'tree>(
2163    field: Node<'tree>,
2164    source: &str,
2165) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2166    if field.kind() != "field_declaration"
2167        || field
2168            .child_by_field_name("type")
2169            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2170    {
2171        return None;
2172    }
2173    let declarator = field.child_by_field_name("declarator")?;
2174    if declarator.kind() != "parenthesized_declarator" {
2175        return None;
2176    }
2177    let opening = declarator.named_child(0)?;
2178    if opening.kind() != "ERROR"
2179        || opening
2180            .named_child(0)
2181            .is_none_or(|word| word.kind() != "identifier")
2182    {
2183        return None;
2184    }
2185    let declarations = stranded_declaration_run(declarator, source).declarations;
2186    (!declarations.is_empty()).then_some(declarations)
2187}
2188
2189/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2190/// export-macro class body leaves in front of the members that macro decorated.
2191///
2192/// The reparse reads the class body as statements, so the attribute becomes a
2193/// call statement of its own -- with the `;` the grammar had to invent -- and
2194/// the members after it are stranded in the `ERROR` that follows.
2195fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2196    let Some(call) = (node.kind() == "expression_statement")
2197        .then(|| node.named_child(0))
2198        .flatten()
2199        .filter(|child| child.kind() == "call_expression")
2200    else {
2201        return false;
2202    };
2203    call.child_by_field_name("function")
2204        .is_some_and(|function| function.kind() == "identifier")
2205        && call
2206            .child_by_field_name("arguments")
2207            .is_some_and(|arguments| {
2208                let mut cursor = arguments.walk();
2209                arguments.named_child_count() > 0
2210                    && arguments
2211                        .named_children(&mut cursor)
2212                        .all(|argument| argument.kind() == "string_literal")
2213            })
2214}
2215
2216/// The start byte of the call the region reparse left where an access-labeled
2217/// constructor was written.
2218///
2219/// A constructor is a member only inside a class body. The export-macro class
2220/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2221/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2222/// comma-expression under `private:`, with no declarator left in the tree. It
2223/// is still spelled in the source, though, starting at the one call under the
2224/// label whose callee is the class's own name. Botan's private six-parameter
2225/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2226/// call is an ambiguity this declines, and so is a reparse from that byte that
2227/// does not yield exactly one constructor declarator there.
2228fn cpp_access_label_constructor_call_start(
2229    node: Node<'_>,
2230    class_name: &str,
2231    source: &str,
2232) -> Option<usize> {
2233    if node.kind() != "labeled_statement" {
2234        return None;
2235    }
2236    let label = node.named_child(0)?;
2237    if label.kind() != "statement_identifier"
2238        || !matches!(
2239            node_text(label, source).trim(),
2240            "public" | "private" | "protected"
2241        )
2242    {
2243        return None;
2244    }
2245    let mut starts = Vec::new();
2246    let mut stack = vec![node];
2247    while let Some(current) = stack.pop() {
2248        if current.kind() == "call_expression"
2249            && current
2250                .child_by_field_name("function")
2251                .is_some_and(|function| {
2252                    function.kind() == "identifier"
2253                        && node_text(function, source).trim() == class_name
2254                })
2255        {
2256            starts.push(current.start_byte());
2257        }
2258        let mut cursor = current.walk();
2259        stack.extend(current.named_children(&mut cursor));
2260    }
2261    let [start] = starts.as_slice() else {
2262        return None;
2263    };
2264    Some(*start)
2265}
2266
2267/// The `function_definition` that gives `declarator` a body, following only the
2268/// declarator chain, so a recovered callable knows whether it is a declaration
2269/// or a definition without anyone having to say.
2270fn cpp_declarator_function_definition<'tree>(
2271    declarator: Node<'tree>,
2272    ancestry: &ParentIndex<'tree>,
2273) -> Option<Node<'tree>> {
2274    let mut current = declarator;
2275    while let Some(parent) = ancestry.parent(current) {
2276        match parent.kind() {
2277            "function_definition" if parent.child_by_field_name("body").is_some() => {
2278                return Some(parent);
2279            }
2280            "pointer_declarator"
2281            | "reference_declarator"
2282            | "parenthesized_declarator"
2283            | "array_declarator" => current = parent,
2284            _ => return None,
2285        }
2286    }
2287    None
2288}
2289
2290/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2291/// declaration walk still reaches, so a recovery that runs ahead of that walk
2292/// would name what it finds without the namespace.
2293fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2294    let mut current = node;
2295    while let Some(parent) = ancestry.parent(current) {
2296        if parent.kind() == "namespace_definition"
2297            && parent.child_by_field_name("body") == Some(current)
2298        {
2299            return true;
2300        }
2301        current = parent;
2302    }
2303    false
2304}
2305
2306/// Whether the source a recovered callable's byte range spells is a definition
2307/// (a declarator with a body) rather than a declaration.
2308///
2309/// A callable recovered from a mangled region owns no node of its own in the
2310/// file's tree, so the occurrence-role scan that climbs from the range to the
2311/// node containing it lands on whatever container the parser left and answers
2312/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2313/// constructor a declaration and left its call site with nothing to navigate to
2314/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2315/// extraction used, and read the answer from the one declaration it spells.
2316///
2317/// `None` when the range is not one recovered declaration on its own, which is
2318/// the case for every ordinary declaration, so callers keep their own answer.
2319pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2320    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2321    let root = tree.root_node();
2322    let mut cursor = root.walk();
2323    let items = root
2324        .named_children(&mut cursor)
2325        .filter(|child| child.kind() != "comment")
2326        .collect::<Vec<_>>();
2327    let [item] = items.as_slice() else {
2328        return None;
2329    };
2330    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2331        return None;
2332    }
2333    match item.kind() {
2334        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2335        "declaration" | "field_declaration" => Some(false),
2336        _ => None,
2337    }
2338}
2339
2340/// Whether `node` is an envelope an attribute-like macro invocation left where
2341/// declarations were written: the declaration-scope `ERROR`
2342/// [`macro_wrapped_declarations`] reads, or the collapsed run
2343/// [`collapsed_macro_declaration_run`] reads.
2344///
2345/// Extraction and resolution must read one definition of this shape. The
2346/// resolver climbs from a declaration's recorded byte range to the node that
2347/// declares it, and a recovered declaration's range lies inside one of these
2348/// envelopes rather than inside a `declaration` node, so the climb stops here
2349/// (the same role `is_recovered_exported_class_container` plays for a recovered
2350/// class).
2351pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2352    let ancestry = ParentIndex::unindexed();
2353    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2354        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2355}
2356
2357fn recover_exported_class_function_definition<'tree>(
2358    node: Node<'tree>,
2359    source: &str,
2360) -> Option<(Node<'tree>, String, Option<Vec<String>>)> {
2361    if node.kind() != "function_definition" {
2362        return None;
2363    }
2364    if let Some(prefix) = node.prev_named_sibling()
2365        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2366        && recovered.range.end_byte == node.end_byte()
2367    {
2368        return Some((node, recovered.name, recovered.raw_supertypes));
2369    }
2370    let type_node = node.child_by_field_name("type")?;
2371    let declarator = node.child_by_field_name("declarator")?;
2372
2373    if matches!(
2374        type_node.kind(),
2375        "class_specifier" | "struct_specifier" | "union_specifier"
2376    ) {
2377        let type_name = type_node
2378            .child_by_field_name("name")
2379            .and_then(|name| direct_identifier_name(name, source));
2380        let exported_macro_type = type_name
2381            .as_ref()
2382            .is_some_and(|name| cpp_export_macro_token(name));
2383        if exported_macro_type {
2384            let mut cursor = node.walk();
2385            let errors_before_declarator = node
2386                .named_children(&mut cursor)
2387                .filter(|child| {
2388                    child.kind() == "ERROR"
2389                        && child.start_byte() >= type_node.end_byte()
2390                        && child.end_byte() <= declarator.start_byte()
2391                })
2392                .collect::<Vec<_>>();
2393            if let Some(name) = errors_before_declarator
2394                .iter()
2395                .find_map(|error| displaced_exported_class_name(*error, source))
2396            {
2397                let raw_supertypes = errors_before_declarator
2398                    .iter()
2399                    .any(|error| malformed_inheritance_syntax(*error))
2400                    .then(|| recovered_malformed_base_name(declarator, source))
2401                    .flatten()
2402                    .map(|base| vec![base]);
2403                return Some((node, name, raw_supertypes));
2404            }
2405            if errors_before_declarator
2406                .iter()
2407                .any(|error| malformed_inheritance_syntax(*error))
2408            {
2409                return None;
2410            }
2411        }
2412        if !exported_macro_type
2413            && let Some(name) = type_name
2414            && !cpp_export_macro_token(&name)
2415            && let Some(base) =
2416                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2417        {
2418            return Some((node, name, Some(vec![base])));
2419        }
2420        if let Some(name) = direct_identifier_name(declarator, source)
2421            && exported_macro_type
2422            && !cpp_export_macro_token(&name)
2423        {
2424            let raw_supertypes = exported_macro_type
2425                .then(|| recovered_single_base_after_declarator(node, declarator, source))
2426                .flatten()
2427                .map(|base| vec![base]);
2428            return Some((node, name, raw_supertypes));
2429        }
2430        if declarator.kind() == "parenthesized_declarator"
2431            && type_node
2432                .child_by_field_name("name")
2433                .and_then(|name| direct_identifier_name(name, source))
2434                .is_some_and(|name| cpp_export_macro_token(&name))
2435        {
2436            if let Some((name, base)) =
2437                recovered_function_like_export_class_owner(declarator, source)
2438            {
2439                return Some((node, name, Some(vec![base])));
2440            }
2441            let body_start = node
2442                .child_by_field_name("body")
2443                .map(|body| body.start_byte())
2444                .unwrap_or(node.end_byte());
2445            let mut cursor = node.walk();
2446            if let Some(name) = node
2447                .named_children(&mut cursor)
2448                .filter(|child| {
2449                    child.kind() == "ERROR"
2450                        && child.start_byte() >= declarator.end_byte()
2451                        && child.end_byte() <= body_start
2452                })
2453                .find_map(|error| declarator_name_from_node(error, source))
2454            {
2455                return Some((node, name, None));
2456            }
2457        }
2458    }
2459
2460    let declarator_text = direct_identifier_name(declarator, source)?;
2461    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
2462        return None;
2463    }
2464    class_identifier_before_body(node, source).map(|name| (node, name, None))
2465}
2466
2467fn recovered_function_like_export_class_owner(
2468    declarator: Node<'_>,
2469    source: &str,
2470) -> Option<(String, String)> {
2471    if declarator.kind() != "parenthesized_declarator" {
2472        return None;
2473    }
2474    let mut cursor = declarator.walk();
2475    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
2476    let [prefix, base] = children.as_slice() else {
2477        return None;
2478    };
2479    if prefix.kind() != "ERROR"
2480        || !matches!(
2481            base.kind(),
2482            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
2483        )
2484    {
2485        return None;
2486    }
2487    let mut identifiers = Vec::new();
2488    let mut prefix_cursor = prefix.walk();
2489    for child in prefix.named_children(&mut prefix_cursor) {
2490        match child.kind() {
2491            "number_literal" | "string_literal" | "char_literal" => {}
2492            "identifier" | "type_identifier" => {
2493                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
2494            }
2495            _ => return None,
2496        }
2497    }
2498    let name = match identifiers.as_slice() {
2499        [name] => name.clone(),
2500        [name, final_token] if final_token == "final" => name.clone(),
2501        _ => return None,
2502    };
2503    if name.is_empty() || cpp_export_macro_token(&name) {
2504        return None;
2505    }
2506    let base = recovered_malformed_base_name(*base, source)?;
2507    Some((name, base))
2508}
2509
2510/// Collect the base names tree-sitter scattered across the recovered head of a
2511/// function-like export-macro class. `skip` names the structural children that
2512/// are not bases, such as the class name and the body. A base arrives either as
2513/// a direct sibling identifier or inside the `ERROR` node the grammar produced
2514/// for a `: public Base` fragment. The grammar leaves the `final` specifier and
2515/// the access specifiers in the same position as the bases, so drop them.
2516/// The bases a recovered function-like export-macro class head names between
2517/// `after` (the end of the class name, or of the access specifier that stands
2518/// in for it) and `before` (the start of the body, or of the `init_declarator`
2519/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
2520/// fragments, and one fragment can also hold the class name and `final`
2521/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
2522/// than by the fragment that holds it.
2523fn recovered_export_head_bases(
2524    node: Node<'_>,
2525    after: usize,
2526    before: usize,
2527    source: &str,
2528) -> Vec<String> {
2529    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
2530    let mut bases = Vec::new();
2531    let mut cursor = node.walk();
2532    for child in node.named_children(&mut cursor) {
2533        if child.kind() == "ERROR" {
2534            let mut error_cursor = child.walk();
2535            bases.extend(
2536                child
2537                    .named_children(&mut error_cursor)
2538                    .filter(within)
2539                    .filter_map(|part| recovered_malformed_base_name(part, source)),
2540            );
2541        } else if within(&child)
2542            && let Some(base) = recovered_malformed_base_name(child, source)
2543        {
2544            bases.push(base);
2545        }
2546    }
2547    bases.retain(|base| {
2548        !matches!(
2549            base.as_str(),
2550            "final" | "public" | "protected" | "private" | "virtual"
2551        )
2552    });
2553    bases
2554}
2555
2556/// Whether `token` is the `final` that closes a recovered class head. The
2557/// grammar keeps it as the `final` keyword when it recognised the head
2558/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
2559/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
2560fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
2561    if token.is_named() {
2562        token.kind() == "identifier" && node_text(token, source) == "final"
2563    } else {
2564        token.kind() == "final"
2565    }
2566}
2567
2568/// The class name of a recovered function-like export-macro class head, read by
2569/// position rather than spelling (#2557). `node` is the sibling tree-sitter
2570/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
2571/// carries the body. The head's tokens, in source order with `ERROR` fragments
2572/// flattened, read `macros... name [final] [: bases...]`: the name is the last
2573/// identifier before the head ends, and the head ends at `final`, at the base
2574/// clause `:`, or at `tail`. Every identifier before the name is decoration
2575/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
2576/// (`X509_CA`) is a class name like any other.
2577///
2578/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
2579/// rule, not a token of the source, so it is never the name.
2580fn recovered_export_head_name<'tree>(
2581    node: Node<'tree>,
2582    tail: Node<'tree>,
2583    source: &str,
2584) -> Option<Node<'tree>> {
2585    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
2586}
2587
2588/// The class name of a head whose tokens straddle the recovered pair: the
2589/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
2590/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
2591/// The positional rule is the same, read over the two token runs in source
2592/// order, so a name in either node wins by position rather than by which node
2593/// holds it.
2594fn recovered_export_pair_head_name<'tree>(
2595    prefix: Node<'tree>,
2596    sibling: Node<'tree>,
2597    tail: Node<'tree>,
2598    source: &str,
2599) -> Option<Node<'tree>> {
2600    let mut tokens = export_head_tokens(prefix, None);
2601    tokens.extend(export_head_tokens(sibling, Some(tail)));
2602    export_head_name_from_tokens(&tokens, source)
2603}
2604
2605/// One node's head tokens in source order, with `ERROR` fragments spliced in
2606/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
2607/// before `tail` when the caller names one.
2608fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
2609    let mut tokens = Vec::new();
2610    let mut cursor = node.walk();
2611    for child in node.children(&mut cursor) {
2612        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
2613            break;
2614        }
2615        if child.kind() == "ERROR" {
2616            let mut fragment_cursor = child.walk();
2617            tokens.extend(child.children(&mut fragment_cursor));
2618        } else {
2619            tokens.push(child);
2620        }
2621    }
2622    tokens
2623}
2624
2625/// The last identifier before the head ends, per the positional rule.
2626fn export_head_name_from_tokens<'tree>(
2627    tokens: &[Node<'tree>],
2628    source: &str,
2629) -> Option<Node<'tree>> {
2630    let mut name = None;
2631    for token in tokens.iter().copied() {
2632        if recovered_export_head_final(token, source) || (!token.is_named() && token.kind() == ":")
2633        {
2634            break;
2635        }
2636        if token.is_named()
2637            && !token.is_missing()
2638            && matches!(
2639                token.kind(),
2640                "identifier" | "type_identifier" | "field_identifier"
2641            )
2642        {
2643            name = Some(token);
2644        }
2645    }
2646    name
2647}
2648
2649/// The `init_declarator` whose `value` carries the class body when the grammar
2650/// keeps a function-like export-macro class head as one `declaration`.
2651fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
2652    let mut cursor = declaration.walk();
2653    declaration
2654        .named_children(&mut cursor)
2655        .find(|child| child.kind() == "init_declarator")
2656}
2657
2658/// Read the bases and the initializer-list body of a `declaration`-shaped tail
2659/// of a function-like export-macro class head. The grammar splits a base list
2660/// across bare declarator fields, `ERROR` fragments, and one trailing
2661/// `init_declarator` whose `value` is the class body. `head_end` is where the
2662/// class identity ends and the bases can begin: the end of the access specifier
2663/// when the class name went to a statement label, and the end of the class name
2664/// itself otherwise.
2665fn recovered_export_declaration_tail<'tree>(
2666    declaration: Node<'tree>,
2667    head_end: usize,
2668    source: &str,
2669) -> Option<(Vec<String>, Node<'tree>)> {
2670    let init = recovered_export_init_declarator(declaration)?;
2671    let body = init.child_by_field_name("value")?;
2672    if body.kind() != "initializer_list" {
2673        return None;
2674    }
2675    let mut bases = recovered_export_head_bases(declaration, head_end, init.start_byte(), source);
2676    bases.extend(recovered_export_head_bases(
2677        init,
2678        init.start_byte(),
2679        body.start_byte(),
2680        source,
2681    ));
2682    Some((bases, body))
2683}
2684
2685/// Whether `node` is the head fragment tree-sitter leaves where
2686/// `class MACRO(args)` was written: the class keyword and the export macro's
2687/// function-like invocation in that order, with nothing else between them.
2688/// Spelling plays no part -- the invocation's `(` following the macro
2689/// identifier directly is what makes it function-like (#2557).
2690///
2691/// Two reductions produce the head. On its own the grammar keeps a body-less
2692/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
2693/// with the invocation's `(`, arguments and `)` beside it. After object-like
2694/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
2695/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
2696/// `declaration` whose class keyword is demoted to a bare `identifier` and
2697/// whose macro invocation becomes the trailing `init_declarator` (#2924).
2698fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
2699    match node.kind() {
2700        "ERROR" => {
2701            let Some(class_node) = first_class_like_child(node) else {
2702                return false;
2703            };
2704            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
2705                return false;
2706            }
2707            if class_node
2708                .child_by_field_name("name")
2709                .and_then(|name| direct_identifier_name(name, source))
2710                .is_none()
2711            {
2712                return false;
2713            }
2714            class_node
2715                .next_sibling()
2716                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
2717        }
2718        "declaration" => {
2719            let mut cursor = node.walk();
2720            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
2721            // The class keyword lost its own node: it survives as a bare
2722            // identifier spelled `class`, `struct` or `union`, which is never a
2723            // declarator or a type in well-formed code. It can sit inside an
2724            // `ERROR` beside the object-like macro names, and an `ERROR` here is
2725            // a grouping failure rather than a construct, so read through it.
2726            let Some(keyword) = children
2727                .iter()
2728                .copied()
2729                .flat_map(|child| {
2730                    let mut cursor = child.walk();
2731                    if child.kind() == "ERROR" {
2732                        child.named_children(&mut cursor).collect::<Vec<_>>()
2733                    } else {
2734                        vec![child]
2735                    }
2736                })
2737                .find(|child| {
2738                    child.kind() == "identifier"
2739                        && matches!(node_text(*child, source), "class" | "struct" | "union")
2740                })
2741            else {
2742                return false;
2743            };
2744            children.last().is_some_and(|init| {
2745                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
2746                    let mut cursor = init.walk();
2747                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
2748                    matches!(parts.as_slice(), [macro_name, arguments]
2749                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
2750                            && arguments.kind() == "argument_list")
2751                }
2752            })
2753        }
2754        _ => false,
2755    }
2756}
2757
2758fn recover_function_like_export_class_pair(
2759    node: Node<'_>,
2760    source: &str,
2761) -> Option<RecoveredFunctionLikeExportClassPair> {
2762    if !is_function_like_export_class_head(node, source) {
2763        return None;
2764    }
2765    let sibling = node.next_named_sibling()?;
2766    let (name, raw_supertypes, body) = match sibling.kind() {
2767        // At translation-unit scope tree-sitter can leave the malformed class
2768        // head in one ERROR node and parse its body as the adjacent compound
2769        // statement. The head still carries the export invocation, displaced
2770        // class name, and optional `final` token in source order, so recover
2771        // the name by the same positional rule as the other shapes.
2772        "compound_statement" => (
2773            recovered_export_pair_head_name(node, sibling, sibling, source)
2774                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
2775            None,
2776            sibling,
2777        ),
2778        "expression_statement" => {
2779            let compound = sibling.named_child(0)?;
2780            if compound.kind() != "compound_literal_expression" {
2781                return None;
2782            }
2783            let body = compound.child_by_field_name("value")?;
2784            if body.kind() != "initializer_list" {
2785                return None;
2786            }
2787            (
2788                compound
2789                    .child_by_field_name("type")
2790                    .and_then(|name| direct_identifier_name(name, source))?,
2791                None,
2792                body,
2793            )
2794        }
2795        "labeled_statement" => {
2796            let label = sibling.child_by_field_name("label")?;
2797            if label.kind() != "statement_identifier" {
2798                return None;
2799            }
2800            let name = normalize_cpp_whitespace(node_text(label, source));
2801            let declaration = sibling
2802                .named_children(&mut sibling.walk())
2803                .find(|child| child.kind() == "declaration")?;
2804            let access = declaration.child_by_field_name("type")?;
2805            if !matches!(
2806                node_text(access, source),
2807                "public" | "protected" | "private"
2808            ) {
2809                return None;
2810            }
2811            let (bases, body) =
2812                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
2813            (name, (!bases.is_empty()).then_some(bases), body)
2814        }
2815        // `class MACRO(2, 0) Name final { ... };`,
2816        // `class MACRO(2, 0) Name final : public Base { ... };`, and
2817        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
2818        // spread over the `type` field, the declarator field, and `ERROR`
2819        // fragments beside them; the name is the last one before the head ends.
2820        "function_definition" => {
2821            let body = sibling.child_by_field_name("body")?;
2822            if body.kind() != "compound_statement" {
2823                return None;
2824            }
2825            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
2826            let bases = recovered_export_head_bases(
2827                sibling,
2828                name_node.end_byte(),
2829                body.start_byte(),
2830                source,
2831            );
2832            (
2833                normalize_cpp_whitespace(node_text(name_node, source)),
2834                (!bases.is_empty()).then_some(bases),
2835                body,
2836            )
2837        }
2838        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
2839        // comma-separated base list makes the grammar keep the whole tail as one
2840        // declaration whose trailing `init_declarator` carries the body.
2841        "declaration" => {
2842            let init = recovered_export_init_declarator(sibling)?;
2843            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
2844            let (bases, body) =
2845                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
2846            (
2847                normalize_cpp_whitespace(node_text(name_node, source)),
2848                (!bases.is_empty()).then_some(bases),
2849                body,
2850            )
2851        }
2852        _ => return None,
2853    };
2854    debug_assert!(
2855        !name.is_empty(),
2856        "a recovered class head names its class by an identifier token"
2857    );
2858    let range = Range {
2859        start_byte: node.start_byte(),
2860        end_byte: sibling.end_byte(),
2861        start_line: node.start_position().row + 1,
2862        end_line: sibling.end_position().row + 1,
2863    };
2864    Some(RecoveredFunctionLikeExportClassPair {
2865        name,
2866        raw_supertypes,
2867        range,
2868        fragmented_body: recovered_fragmented_export_body(body, range)?,
2869    })
2870}
2871
2872/// Recover a function-like export-macro class that tree-sitter embedded in a
2873/// larger error after an earlier malformed class body. The grammar still
2874/// preserves every part of the class head: the `class` token, export macro
2875/// identifier and argument list, displaced class identifier, access specifier,
2876/// base field, and initializer-list-shaped body. Match only that complete
2877/// structured sequence and keep each recovered class's exact byte envelope.
2878fn recover_embedded_function_like_export_classes(
2879    node: Node<'_>,
2880    source: &str,
2881) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
2882    if !node.is_error() {
2883        return Vec::new();
2884    }
2885
2886    let mut nodes = Vec::new();
2887    let mut class_tokens = Vec::new();
2888    let mut stack = vec![node];
2889    while let Some(current) = stack.pop() {
2890        nodes.push(current);
2891        // An error subtree without an anonymous class token cannot contain
2892        // a recoverable class head. Detect that before sorting or resolving
2893        // kind-id tables; the grammar spelling matches all aliased symbol ids.
2894        if !current.is_named() && current.kind() == "class" {
2895            class_tokens.push(current);
2896        }
2897        push_children_reversed(current, &mut stack);
2898    }
2899    if class_tokens.is_empty() {
2900        return Vec::new();
2901    }
2902    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2903    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
2904
2905    // The scans below ask the same handful of kind questions once per node of
2906    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
2907    let language = node.language();
2908    let class_kind = NodeKindIds::new(&language, "class");
2909    let identifier_kinds = [
2910        NodeKindIds::new(&language, "identifier"),
2911        NodeKindIds::new(&language, "type_identifier"),
2912        NodeKindIds::new(&language, "field_identifier"),
2913    ];
2914    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
2915    let colon_kind = NodeKindIds::new(&language, ":");
2916    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
2917
2918    let mut recovered = Vec::new();
2919    for class_token in class_tokens {
2920        debug_assert!(class_kind.matches(class_token));
2921        let row = class_token.start_position().row;
2922        // The head after the `class` token reads `MACRO(args) macros... name
2923        // [final] : bases`. The macro is the first identifier, a function-like
2924        // invocation when its argument list is the next thing after it, and the
2925        // name is the last identifier before the head ends at `final` or at the
2926        // base clause `:`. Spelling plays no part (#2557).
2927        let is_identifier = |candidate: &Node<'_>| {
2928            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
2929        };
2930        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
2931            candidate.start_byte() >= class_token.end_byte()
2932                && candidate.start_position().row == row
2933                && is_identifier(candidate)
2934        }) else {
2935            continue;
2936        };
2937        let Some(arguments) = nodes.iter().copied().find(|candidate| {
2938            argument_list_kind.matches(*candidate)
2939                && candidate.start_byte() >= macro_name.end_byte()
2940                && candidate.start_position().row == row
2941        }) else {
2942            continue;
2943        };
2944        if nodes.iter().any(|candidate| {
2945            is_identifier(candidate)
2946                && candidate.start_byte() >= macro_name.end_byte()
2947                && candidate.end_byte() <= arguments.start_byte()
2948        }) {
2949            continue;
2950        }
2951        let Some(head_end) = nodes.iter().copied().find(|candidate| {
2952            candidate.start_byte() >= arguments.end_byte()
2953                && (recovered_export_head_final(*candidate, source)
2954                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
2955        }) else {
2956            continue;
2957        };
2958        let Some(name_node) = nodes.iter().copied().rfind(|candidate| {
2959            is_identifier(candidate)
2960                && candidate.start_byte() >= arguments.end_byte()
2961                && candidate.end_byte() <= head_end.start_byte()
2962                && candidate.start_position().row == row
2963        }) else {
2964            continue;
2965        };
2966        let name = normalize_cpp_whitespace(node_text(name_node, source));
2967        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
2968            field_initializer_kind.matches(*candidate)
2969                && candidate.start_byte() >= name_node.end_byte()
2970                && candidate
2971                    .child_by_field_name("field")
2972                    .or_else(|| candidate.named_child(0))
2973                    .is_some()
2974                && candidate
2975                    .child_by_field_name("value")
2976                    .or_else(|| {
2977                        let mut cursor = candidate.walk();
2978                        candidate
2979                            .named_children(&mut cursor)
2980                            .find(|child| child.kind() == "initializer_list")
2981                    })
2982                    .is_some_and(|value| value.kind() == "initializer_list")
2983        }) else {
2984            continue;
2985        };
2986        let has_access = nodes.iter().copied().any(|candidate| {
2987            candidate.start_byte() >= name_node.end_byte()
2988                && candidate.end_byte() <= base_initializer.start_byte()
2989                && matches!(
2990                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
2991                    "public" | "protected" | "private"
2992                )
2993        });
2994        if !has_access {
2995            continue;
2996        }
2997        let Some(base_node) = base_initializer
2998            .child_by_field_name("field")
2999            .or_else(|| base_initializer.named_child(0))
3000        else {
3001            continue;
3002        };
3003        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3004            continue;
3005        };
3006        let body = base_initializer
3007            .child_by_field_name("value")
3008            .or_else(|| {
3009                let mut cursor = base_initializer.walk();
3010                base_initializer
3011                    .named_children(&mut cursor)
3012                    .find(|child| child.kind() == "initializer_list")
3013            })
3014            .expect("initializer-list value checked above");
3015        let range = Range {
3016            start_byte: class_token.start_byte(),
3017            end_byte: body.end_byte(),
3018            start_line: class_token.start_position().row + 1,
3019            end_line: body.end_position().row + 1,
3020        };
3021        if recovered
3022            .iter()
3023            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3024                existing.name == name && existing.range == range
3025            })
3026        {
3027            continue;
3028        }
3029        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3030            name,
3031            range,
3032            raw_supertypes: vec![base],
3033            fragmented_body: match recovered_fragmented_export_body(body, range) {
3034                Some(fragmented) => fragmented,
3035                None => continue,
3036            },
3037        });
3038    }
3039    recovered
3040}
3041
3042fn lifted_function_like_export_class_namespace<'tree>(
3043    node: Node<'tree>,
3044    source: &str,
3045    ancestry: &ParentIndex<'tree>,
3046) -> Option<String> {
3047    // A long malformed body can embed the next exported class several levels
3048    // below a bogus top-level function_definition. Compare namespace evidence
3049    // against that top-level envelope, not only the recovered ERROR's direct
3050    // parent. The source tree still proves the same boundary: one earlier
3051    // malformed namespace and one later standalone closing brace.
3052    let mut anchor = node;
3053    let parent = loop {
3054        let parent = ancestry.parent(anchor)?;
3055        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3056            break parent;
3057        }
3058        anchor = parent;
3059    };
3060    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3061        sibling.start_byte() > anchor.end_byte()
3062            && sibling.kind() == "ERROR"
3063            && sibling.named_child_count() == 0
3064            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3065    });
3066    if !has_later_close {
3067        return None;
3068    }
3069    let candidates = parent
3070        .named_children(&mut parent.walk())
3071        .filter(|sibling| {
3072            sibling.kind() == "namespace_definition"
3073                && sibling.has_error()
3074                && sibling.end_byte() < anchor.start_byte()
3075        })
3076        .filter_map(|namespace| {
3077            namespace
3078                .child_by_field_name("name")
3079                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3080                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3081        })
3082        .collect::<Vec<_>>();
3083    let [namespace] = candidates.as_slice() else {
3084        return None;
3085    };
3086    Some(namespace.clone())
3087}
3088
3089pub(crate) fn recovered_function_like_export_class_pair_has_body(
3090    node: Node<'_>,
3091    source: &str,
3092    identifier: &str,
3093    range: &Range,
3094) -> bool {
3095    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3096        recovered.name == identifier
3097            && recovered.range.start_byte == range.start_byte
3098            && recovered.range.end_byte == range.end_byte
3099    })
3100}
3101
3102/// One file's embedded export-macro class recovery, resolved once and keyed by
3103/// the `ERROR` node that mints each set.
3104///
3105/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3106/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3107/// once per class-like unit in the file. On a translation unit the parser could
3108/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3109/// node spans most of the file -- that is one full subtree pass per class,
3110/// quadratic in the file's size, and it was 78% of that file's inverse scan
3111/// (#1496).
3112///
3113/// Keyed by byte span rather than node identity, so one analyzer generation's
3114/// re-parses of the same content share the index. A nested `ERROR` that shares
3115/// its parent's span recovers the same classes: the only node the parent adds
3116/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3117#[derive(Default)]
3118pub struct CppRecoveredExportClassIndex {
3119    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3120}
3121
3122impl CppRecoveredExportClassIndex {
3123    pub fn build(root: Node<'_>, source: &str) -> Self {
3124        let mut by_error_node: HashMap<
3125            (usize, usize),
3126            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3127        > = HashMap::default();
3128        // One cursor for the whole-file walk, and the error question asked by
3129        // symbol rather than by `kind()` string (#3097).
3130        let mut cursor = root.walk();
3131        let mut stack = vec![root];
3132        while let Some(node) = stack.pop() {
3133            if node.is_error() {
3134                let recovered = recover_embedded_function_like_export_classes(node, source);
3135                if !recovered.is_empty() {
3136                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3137                }
3138            }
3139            stack.extend(node.named_children(&mut cursor));
3140        }
3141        Self { by_error_node }
3142    }
3143
3144    /// The bytes this index holds, for the analyzer cache's weight.
3145    pub fn approximate_size(&self) -> usize {
3146        self.by_error_node
3147            .values()
3148            .fold(0usize, |total, recovered| {
3149                recovered.iter().fold(
3150                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3151                    |acc, class| {
3152                        acc.saturating_add(std::mem::size_of::<
3153                            RecoveredEmbeddedFunctionLikeExportClass,
3154                        >())
3155                        .saturating_add(class.name.len())
3156                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3157                    },
3158                )
3159            })
3160    }
3161
3162    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3163        self.by_error_node
3164            .get(&(node.start_byte(), node.end_byte()))
3165            .is_some_and(|recovered| {
3166                recovered.iter().any(|class| {
3167                    class.name == identifier
3168                        && class.range.start_byte == range.start_byte
3169                        && class.range.end_byte == range.end_byte
3170                })
3171            })
3172    }
3173}
3174
3175// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3176// `recovered_class_body_at` pops while deciding whether a recovered class shape
3177// claims one declaration range. The count is deterministic for a given source,
3178// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3179// pins it directly instead of timing the walk, the way #2358 pinned the
3180// `remove_code_unit` scan.
3181#[cfg(any(test, feature = "test-support"))]
3182thread_local! {
3183    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3184        const { std::cell::Cell::new(0) };
3185}
3186
3187/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3188/// the calling thread since the last
3189/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3190#[cfg(any(test, feature = "test-support"))]
3191#[doc(hidden)]
3192pub fn recovered_class_body_node_visits_for_test() -> usize {
3193    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3194}
3195
3196/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3197#[cfg(any(test, feature = "test-support"))]
3198#[doc(hidden)]
3199pub fn reset_recovered_class_body_node_visits_for_test() {
3200    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3201}
3202
3203#[cfg(any(test, feature = "test-support"))]
3204fn record_recovered_class_body_visit() {
3205    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3206}
3207
3208#[cfg(not(any(test, feature = "test-support")))]
3209fn record_recovered_class_body_visit() {}
3210
3211/// Whether a recovered class shape named `identifier` owns `range`, and if so
3212/// whether that shape has a body.
3213///
3214/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3215/// forward declaration, and `None` means no recovered shape claims the range,
3216/// so the caller reads the plain `class_specifier` family instead. This is the
3217/// single definition of "does a recovered class have a body": the resolver's
3218/// declaration-strength answer and the navigation occurrence role both read it,
3219/// so an export-macro class is a definition on both paths.
3220///
3221/// The walk follows only the nodes whose span covers `range.start_byte`, which
3222/// is every node that can answer. Each recovered shape reports a range that
3223/// starts at the node's own start byte (the function-like export pair, whose
3224/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3225/// class, which the caller gates on an equal start) or at a token inside the
3226/// node (the embedded export class, keyed on its `class` token, and the
3227/// exported class wrapper, gated on containment here), and every one of them is
3228/// accepted only on an exact match with `range`. Descending everywhere instead
3229/// made one declaration-strength question cost a full pass over the file, so
3230/// asking it once per reference was quadratic in file size: 80% of the 385 s
3231/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3232/// (#1496).
3233pub(crate) fn recovered_class_body_at(
3234    recovered_export_classes: &CppRecoveredExportClassIndex,
3235    root: Node<'_>,
3236    source: &str,
3237    identifier: &str,
3238    range: &Range,
3239) -> Option<bool> {
3240    let covers_range_start = |node: &Node<'_>| {
3241        node.start_byte() <= range.start_byte
3242            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3243    };
3244    let mut stack = vec![root];
3245    let mut saw_forward = false;
3246    while let Some(node) = stack.pop() {
3247        record_recovered_class_body_visit();
3248        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3249        // so an unequal start settles it before the recovery reads the node's
3250        // children at all.
3251        if (node.start_byte() == range.start_byte
3252            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3253            || recovered_export_classes.claims(node, identifier, range)
3254            || (node.start_byte() == range.start_byte
3255                && recovered_fragmented_class_has_body(node, source, identifier, range))
3256        {
3257            return Some(true);
3258        }
3259        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3260            return Some(true);
3261        }
3262        // Macro-decorated exported classes are recovered from a malformed
3263        // function_definition/declaration wrapper. Their indexed class range starts at
3264        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3265        // may also extend the indexed range beyond the wrapper through trailing class
3266        // fragments. Match the structured container that owns the range start by its
3267        // recovered name instead of requiring identical boundaries.
3268        if node.start_byte() <= range.start_byte
3269            && range.start_byte < node.end_byte()
3270            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3271        {
3272            if has_body {
3273                return Some(true);
3274            }
3275            saw_forward = true;
3276            continue;
3277        }
3278        let mut cursor = node.walk();
3279        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3280    }
3281    saw_forward.then_some(false)
3282}
3283
3284/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3285/// head of `identifier`'s member list at `range`.
3286///
3287/// An aggregate whose member list opens with a field-list macro invocation
3288/// never forms a specifier, so the ordinary strength read finds no specifier
3289/// node at the indexed range and answers `Unknown`. A complete definition
3290/// recovered this way then loses the defining-type choice to a forward
3291/// declaration of the same tag in another header, and every member the
3292/// definition declares becomes unreachable through it: libuv's
3293/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3294/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3295/// proves the body, so it answers for its own ranges.
3296fn recovered_collapsed_aggregate_has_body(
3297    node: Node<'_>,
3298    source: &str,
3299    identifier: &str,
3300    range: &Range,
3301) -> bool {
3302    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3303        head.key.start_byte() == range.start_byte
3304            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3305    };
3306    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3307        return true;
3308    }
3309    if !node.is_error() {
3310        return false;
3311    }
3312    let mut cursor = node.walk();
3313    let children = node.children(&mut cursor).collect::<Vec<_>>();
3314    children
3315        .iter()
3316        .enumerate()
3317        .filter(|(_, child)| {
3318            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3319        })
3320        .any(|(index, _)| {
3321            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3322        })
3323}
3324
3325/// Whether `node` is the base type displaced into the declarator field of an
3326/// export-macro class that tree-sitter represented as a declaration or
3327/// function definition.
3328///
3329/// Declaration extraction already recovers this exact malformed envelope as a
3330/// class and records the declarator as its base. Reference extraction must use
3331/// the same structural fact instead of treating the node as a function name.
3332pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3333    if !matches!(
3334        node.kind(),
3335        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3336    ) {
3337        return false;
3338    }
3339    if let Some(function) = node.parent().filter(|parent| {
3340        parent.kind() == "function_definition"
3341            && parent
3342                .child_by_field_name("declarator")
3343                .is_some_and(|declarator| same_node(declarator, node))
3344    }) {
3345        return recover_exported_class_function_definition(function, source)
3346            .is_some_and(|(_, _, raw_supertypes)| raw_supertypes.is_some());
3347    }
3348    let Some(initializer) = node.parent().filter(|parent| {
3349        parent.kind() == "init_declarator"
3350            && parent
3351                .child_by_field_name("declarator")
3352                .is_some_and(|declarator| same_node(declarator, node))
3353    }) else {
3354        return false;
3355    };
3356    initializer
3357        .parent()
3358        .filter(|parent| parent.kind() == "declaration")
3359        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
3360        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
3361}
3362
3363/// Recover the class item from a region reparse that still carries the
3364/// sentinel's synthetic function envelope.  An unknown class attribute can
3365/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
3366/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
3367/// then nested below that function, so direct class-child lookup is not enough.
3368struct CppSentinelReparsedClass<'tree> {
3369    declaration_node: Node<'tree>,
3370    name: String,
3371    body: Node<'tree>,
3372    raw_supertypes: Option<Vec<String>>,
3373}
3374
3375fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
3376    let mut cursor = root.walk();
3377    root.named_children(&mut cursor)
3378        .find(|child| child.kind() != "comment")
3379        .filter(|child| child.kind() == "template_declaration")
3380}
3381
3382fn cpp_sentinel_reparsed_class<'tree>(
3383    root: Node<'tree>,
3384    template_node: Option<Node<'tree>>,
3385    source: &str,
3386    ancestry: &ParentIndex<'tree>,
3387) -> Option<CppSentinelReparsedClass<'tree>> {
3388    let container = template_node.unwrap_or(root);
3389    let mut cursor = container.walk();
3390    for child in container.named_children(&mut cursor) {
3391        if matches!(
3392            child.kind(),
3393            "class_specifier" | "struct_specifier" | "union_specifier"
3394        ) {
3395            let name = class_like_name(child, source, ancestry)?;
3396            let body = cpp_body_node(child)?;
3397            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
3398                .then(|| extract_cpp_supertypes(child, source));
3399            return Some(CppSentinelReparsedClass {
3400                declaration_node: child,
3401                name,
3402                body,
3403                raw_supertypes,
3404            });
3405        }
3406        if child.kind() == "declaration"
3407            && let Some(class_node) = first_class_like_child(child)
3408        {
3409            let name = class_like_name(class_node, source, ancestry)?;
3410            let body = cpp_body_node(class_node)?;
3411            let raw_supertypes =
3412                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3413                    .then(|| extract_cpp_supertypes(class_node, source));
3414            return Some(CppSentinelReparsedClass {
3415                declaration_node: class_node,
3416                name,
3417                body,
3418                raw_supertypes,
3419            });
3420        }
3421        // Only when the nested class item carries its own body. A bodyless
3422        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
3423        // a function definition -- is the export-macro shape recovered by the
3424        // next arm, and must fall through to it rather than abort the search.
3425        if child.kind() == "function_definition"
3426            && let Some(class_node) = first_class_like_child(child)
3427            && let Some(body) = cpp_body_node(class_node)
3428            && let Some(name) = class_like_name(class_node, source, ancestry)
3429        {
3430            let raw_supertypes =
3431                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
3432                    .then(|| extract_cpp_supertypes(class_node, source));
3433            return Some(CppSentinelReparsedClass {
3434                declaration_node: class_node,
3435                name,
3436                body,
3437                raw_supertypes,
3438            });
3439        }
3440        if child.kind() == "function_definition"
3441            && let Some((_, name, raw_supertypes)) =
3442                recover_exported_class_function_definition(child, source)
3443        {
3444            let body = cpp_body_node(child)?;
3445            return Some(CppSentinelReparsedClass {
3446                declaration_node: child,
3447                name,
3448                body,
3449                raw_supertypes,
3450            });
3451        }
3452    }
3453    None
3454}
3455
3456fn recovered_postfix_export_macro_base(
3457    node: Node<'_>,
3458    type_node: Node<'_>,
3459    declarator: Node<'_>,
3460    source: &str,
3461) -> Option<String> {
3462    let mut cursor = node.walk();
3463    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
3464        child.kind() == "ERROR"
3465            && child.start_byte() >= type_node.end_byte()
3466            && child.end_byte() <= declarator.start_byte()
3467            && postfix_export_macro_inheritance(*child, source)
3468    });
3469    malformed_clauses.next()?;
3470    if malformed_clauses.next().is_some() {
3471        return None;
3472    }
3473    recovered_malformed_base_name(declarator, source)
3474}
3475
3476fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
3477    let mut macro_count = 0;
3478    let mut colon_count = 0;
3479    let mut access_count = 0;
3480    for index in 0..node.child_count() {
3481        let Some(child) = node.child(index) else {
3482            return false;
3483        };
3484        match child.kind() {
3485            "identifier" | "type_identifier" if child.is_named() => {
3486                let candidate = normalize_cpp_whitespace(node_text(child, source));
3487                if !cpp_export_macro_token(&candidate) {
3488                    return false;
3489                }
3490                macro_count += 1;
3491            }
3492            ":" if !child.is_named() => colon_count += 1,
3493            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
3494            _ => return false,
3495        }
3496    }
3497    macro_count == 1 && colon_count == 1 && access_count == 1
3498}
3499
3500fn recovered_single_base_after_declarator(
3501    node: Node<'_>,
3502    declarator: Node<'_>,
3503    source: &str,
3504) -> Option<String> {
3505    let body_start = node
3506        .child_by_field_name("body")
3507        .map(|body| body.start_byte())
3508        .unwrap_or(node.end_byte());
3509    let mut cursor = node.walk();
3510    let mut bases = node
3511        .named_children(&mut cursor)
3512        .filter(|child| {
3513            child.kind() == "ERROR"
3514                && child.start_byte() >= declarator.end_byte()
3515                && child.end_byte() <= body_start
3516        })
3517        .filter_map(|error| displaced_exported_class_name(error, source));
3518    let base = bases.next()?;
3519    bases.next().is_none().then_some(base)
3520}
3521
3522fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
3523    (0..node.child_count()).any(|index| {
3524        node.child(index)
3525            .is_some_and(|child| matches!(child.kind(), ":" | "public" | "protected" | "private"))
3526    })
3527}
3528
3529pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
3530    recover_exported_class_function_definition(node, source).is_some()
3531}
3532
3533fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
3534    matches!(
3535        kind,
3536        "ERROR"
3537            | "preproc_if"
3538            | "preproc_ifdef"
3539            | "preproc_ifndef"
3540            | "preproc_else"
3541            | "preproc_elif"
3542    ) || (kind == "labeled_statement" && in_class_scope)
3543}
3544
3545pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
3546    if node.kind() != "declaration" {
3547        return false;
3548    }
3549    let mut ancestor = node.parent();
3550    while let Some(container) = ancestor {
3551        match container.kind() {
3552            "compound_statement" => {
3553                return container.parent().is_some_and(|class_container| {
3554                    is_recovered_exported_class_container(class_container, source)
3555                });
3556            }
3557            // These containers preserve ScopeInfo in visit_node. declaration_list is
3558            // the body container selected for a linkage specification.
3559            "template_declaration" | "linkage_specification" | "declaration_list" => {}
3560            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
3561            _ => return false,
3562        }
3563        ancestor = container.parent();
3564    }
3565    false
3566}
3567
3568pub fn recovered_exported_class_has_body(
3569    node: Node<'_>,
3570    source: &str,
3571    expected_name: &str,
3572) -> Option<bool> {
3573    match node.kind() {
3574        "function_definition" => {
3575            let (class_node, name, _) = recover_exported_class_function_definition(node, source)?;
3576            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3577        }
3578        "declaration" | "field_declaration" => {
3579            let recovered = recover_exported_class_declaration(node, source)?;
3580            (recovered.name == expected_name).then(|| recovered.body.is_some())
3581        }
3582        _ => None,
3583    }
3584}
3585
3586fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
3587    let body_start = node
3588        .child_by_field_name("body")
3589        .map(|body| body.start_byte())
3590        .unwrap_or(node.end_byte());
3591    let mut stack = Vec::new();
3592    for index in (0..node.named_child_count()).rev() {
3593        let Some(child) = node.named_child(index) else {
3594            continue;
3595        };
3596        if child.start_byte() >= body_start {
3597            continue;
3598        }
3599        stack.push(child);
3600    }
3601
3602    let mut best = None;
3603    while let Some(current) = stack.pop() {
3604        if matches!(current.kind(), "identifier" | "type_identifier") {
3605            let name = normalize_cpp_whitespace(node_text(current, source));
3606            if !name.is_empty()
3607                && !cpp_export_macro_token(&name)
3608                && !matches!(name.as_str(), "class" | "struct" | "union")
3609            {
3610                best = Some(name);
3611            }
3612            continue;
3613        }
3614
3615        for index in (0..current.named_child_count()).rev() {
3616            if let Some(child) = current.named_child(index)
3617                && child.start_byte() < body_start
3618            {
3619                stack.push(child);
3620            }
3621        }
3622    }
3623    best
3624}
3625
3626fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3627    if node.kind() == "declaration"
3628        && node
3629            .child_by_field_name("type")
3630            .or_else(|| first_class_like_child(node))
3631            .is_some_and(|type_node| {
3632                matches!(
3633                    type_node.kind(),
3634                    "class_specifier" | "struct_specifier" | "union_specifier"
3635                )
3636            })
3637        && let Some(name) = node
3638            .child_by_field_name("declarator")
3639            .and_then(|declarator| declarator_name_from_node(declarator, source))
3640        && !cpp_export_macro_token(&name)
3641    {
3642        return Some(name);
3643    }
3644
3645    if node.kind() == "function_definition"
3646        && node.child_by_field_name("type").is_some_and(|type_node| {
3647            matches!(
3648                type_node.kind(),
3649                "class_specifier" | "struct_specifier" | "union_specifier"
3650            )
3651        })
3652        && let Some(name) = node
3653            .child_by_field_name("declarator")
3654            .and_then(|declarator| direct_identifier_name(declarator, source))
3655        && !cpp_export_macro_token(&name)
3656    {
3657        return Some(name);
3658    }
3659
3660    let class_node = if matches!(
3661        node.kind(),
3662        "class_specifier" | "struct_specifier" | "union_specifier"
3663    ) {
3664        node
3665    } else {
3666        first_class_like_child(node)?
3667    };
3668    class_like_name_from_children(class_node, source)
3669}
3670
3671fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
3672    if !matches!(
3673        node.kind(),
3674        "identifier" | "field_identifier" | "type_identifier"
3675    ) {
3676        return None;
3677    }
3678    let name = normalize_cpp_whitespace(node_text(node, source));
3679    (!name.is_empty()).then_some(name)
3680}
3681
3682fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
3683    match node.kind() {
3684        "identifier" | "field_identifier" | "type_identifier" => {
3685            let name = normalize_cpp_whitespace(node_text(node, source));
3686            (!name.is_empty()).then_some(name)
3687        }
3688        _ => {
3689            let mut cursor = node.walk();
3690            node.named_children(&mut cursor)
3691                .find_map(|child| declarator_name_from_node(child, source))
3692        }
3693    }
3694}
3695
3696fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
3697    let mut cursor = node.walk();
3698    node.named_children(&mut cursor).find(|child| {
3699        matches!(
3700            child.kind(),
3701            "class_specifier" | "struct_specifier" | "union_specifier"
3702        )
3703    })
3704}
3705
3706/// Push a container's children as a `Siblings` cursor rather than snapshotting
3707/// them all with one shared scope: children are visited one at a time so a
3708/// `using namespace X;` sibling can affect the scope threaded to the siblings
3709/// that textually follow it (issue #1093).
3710fn push_cpp_container_work<'tree>(
3711    node: Node<'tree>,
3712    scope: ScopeInfo,
3713    stack: &mut Vec<CppWork<'tree>>,
3714) {
3715    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
3716}
3717
3718/// Materialize one selected named-child range with a tree-sitter cursor. The
3719/// cursor advances linearly across the parent's concrete children; repeatedly
3720/// asking for `named_child(index)` is quadratic on very wide generated nodes.
3721fn push_cpp_sibling_range<'tree>(
3722    parent: Node<'tree>,
3723    start_index: usize,
3724    end_index: usize,
3725    scope: ScopeInfo,
3726    stack: &mut Vec<CppWork<'tree>>,
3727) {
3728    let mut cursor = parent.walk();
3729    let children = parent
3730        .named_children(&mut cursor)
3731        .skip(start_index)
3732        .take(end_index.saturating_sub(start_index))
3733        .collect::<Vec<_>>()
3734        .into_iter();
3735    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
3736}
3737
3738/// Advance a `Siblings` cursor by one child: dispatch the current child under
3739/// the scope accumulated from its *earlier* siblings, then push a
3740/// continuation for the remaining siblings carrying the scope updated for
3741/// *this* child (only `using namespace X;` directives change it). Pushing the
3742/// continuation before the current child's own node work means the current
3743/// child's subtree fully drains (LIFO) before the next sibling is visited,
3744/// preserving left-to-right order.
3745fn advance_cpp_siblings<'tree>(
3746    mut siblings: CppSiblingsWork<'tree>,
3747    source: &str,
3748    stack: &mut Vec<CppWork<'tree>>,
3749) {
3750    let Some(child) = siblings.children.next() else {
3751        return;
3752    };
3753    let current_scope = siblings.scope.clone();
3754    if let Some(namespace) = cpp_using_namespace_target(child, source) {
3755        siblings.scope.visible_using_namespaces.push(namespace);
3756    }
3757    if !siblings.children.as_slice().is_empty() {
3758        stack.push(CppWork::Siblings(siblings));
3759    }
3760    stack.push(CppWork::Node(CppNodeWork {
3761        node: child,
3762        scope: current_scope,
3763    }));
3764}
3765
3766/// The namespace target of a `using namespace X;` directive, or `None` for
3767/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
3768/// kind. Distinguished structurally by the presence of the grammar's literal
3769/// `namespace` keyword token among the node's children -- not by inspecting
3770/// source text -- so it never misreads a member-importing using-declaration
3771/// as a namespace directive.
3772fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
3773    if node.kind() != "using_declaration" {
3774        return None;
3775    }
3776    let mut cursor = node.walk();
3777    let is_namespace_directive = node
3778        .children(&mut cursor)
3779        .any(|child| child.kind() == "namespace");
3780    if !is_namespace_directive {
3781        return None;
3782    }
3783    let target = node.named_child(0)?;
3784    // A leading `::` is the explicit-global marker, not part of the namespace
3785    // path (`using namespace ::std::chrono;`). Drop that AST token before
3786    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
3787    // keeps: storing the marker verbatim desynced the legacy package string from
3788    // the FqName bridge, which splits on `::` and drops the empty leading
3789    // component, tripping the package/short boundary assert when a bare-owner
3790    // out-of-line definition borrowed the directive's namespace (#1093 path).
3791    let start = target
3792        .child(0)
3793        .filter(|child| !child.is_named() && child.kind() == "::")
3794        .map_or(target.start_byte(), |marker| marker.end_byte());
3795    let text = normalize_cpp_whitespace(
3796        source
3797            .get(start..target.end_byte())
3798            .expect("using-directive target covers one source range"),
3799    );
3800    (!text.is_empty()).then_some(text)
3801}
3802
3803/// Every `using namespace X;` directive target in a file, in source order, for
3804/// resolution-time consumers that need the file's using-directives without the
3805/// per-position scope threading extraction does. Parses `source` fresh and
3806/// walks the tree structurally, reusing `cpp_using_namespace_target` (which
3807/// keys on the grammar's `namespace` keyword token, not source text), so it
3808/// never misreads a member-importing `using X::Y;` as a namespace directive.
3809///
3810/// This is a whole-file over-approximation of what is in scope at any one point
3811/// (a directive nested inside a `namespace {}` block or a function body is still
3812/// reported), which is exactly what the #1134 identity reconciler wants: extra
3813/// candidate namespaces that no visible class confirms are harmless, and two
3814/// that both confirm are treated as a genuine ambiguity by the reconciler.
3815pub fn cpp_file_using_namespaces(source: &str) -> Vec<String> {
3816    let mut parser = Parser::new();
3817    if parser
3818        .set_language(&tree_sitter_cpp::LANGUAGE.into())
3819        .is_err()
3820    {
3821        return Vec::new();
3822    }
3823    let Some(tree) = parser.parse(source, None) else {
3824        return Vec::new();
3825    };
3826    let mut namespaces = Vec::new();
3827    let mut seen = std::collections::HashSet::new();
3828    let mut stack = vec![tree.root_node()];
3829    while let Some(node) = stack.pop() {
3830        if let Some(namespace) = cpp_using_namespace_target(node, source)
3831            && seen.insert(namespace.clone())
3832        {
3833            namespaces.push(namespace);
3834        }
3835        let mut cursor = node.walk();
3836        stack.extend(node.named_children(&mut cursor));
3837    }
3838    namespaces
3839}
3840
3841pub struct CppVisitor<'a> {
3842    pub file: &'a ProjectFile,
3843    pub source: &'a str,
3844    pub parsed: &'a mut ParsedFile,
3845    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
3846    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
3847    ///
3848    /// C has no nested tag scope: a struct/union/enum tag declared inside
3849    /// another aggregate's member list has the scope of the outer declaration
3850    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
3851    /// therefore declares a file-scope `inner` that a later file-scope
3852    /// `struct inner *p;` legitimately references, where C++ would make the
3853    /// same shape a nested class `outer::inner`. Headers carry no compilation
3854    /// language of their own and keep the conservative C++ interpretation.
3855    pub c_tag_semantics: bool,
3856    /// Sticky evidence that this C++ walk reached a declaration whose scope
3857    /// would change under C tag semantics, including visitor recovery work.
3858    pub(crate) c_tag_scope_witness: bool,
3859    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
3860    /// Byte regions whose contents were re-owned by a fragmented export-class
3861    /// recovery (#938): the scattered members between the fragmented
3862    /// declaration and its displaced closing brace are indexed as members of
3863    /// the recovered class by the region reparse, so the ordinary sibling walk
3864    /// must not ALSO index them as top-level declarations (that double-indexing
3865    /// made a scattered nested class ambiguous between `Inner` and
3866    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
3867    /// linear scan at visit time is fine.
3868    pub consumed_fragment_regions: Vec<(usize, usize)>,
3869    /// Lexical namespace corrections and brace boundaries for declarations
3870    /// displaced by parse recovery (issues #1537 and #3087). The parsed
3871    /// ancestors can stop short or extend past their true closes; see
3872    /// [`CppVisitor::recovered_namespace_scope`].
3873    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
3874    /// Owned reparses waiting for the outer work loop. Partitioning another
3875    /// swallowed class in a tail must not grow the Rust call stack.
3876    pub partitioned_regions: Vec<(Tree, std::ops::Range<usize>, ScopeInfo)>,
3877    /// The namespace forward declarations already folded out of each tree this
3878    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
3879    /// Empty until the first question, which the overwhelming majority of files
3880    /// never ask.
3881    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
3882    /// Which owners already have field declarations in the parse product, as
3883    /// [`CppVisitor::has_enum_enumerator_units`] needs to know. `None` until
3884    /// the first enum asks, which most files never do (#2786).
3885    pub field_owners: Option<CppFieldOwnerIndex>,
3886    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
3887    /// happen to the declaration set, innermost last. Empty outside a recovery
3888    /// reparse, which is almost always (#2787).
3889    pub recovery_captures: Vec<CppRecoveryCapture>,
3890    /// Object-like field-list macros defined earlier in this source. Their
3891    /// replacements are parsed structurally and materialized under each
3892    /// invoking aggregate; no source-text expansion is used.
3893    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
3894    /// Names whose active replacement is not a unique structured field list.
3895    /// A later `#undef` resets the ambiguity; another `#define` does not.
3896    pub ambiguous_object_macro_fields: HashSet<String>,
3897}
3898
3899/// One source-order event from an object-like field-list macro environment.
3900///
3901/// The event stream lets an include-closure consumer preserve preprocessor
3902/// invalidation without guessing which parsed header happened to be visited
3903/// first. A conservative consumer may permanently block a name after an
3904/// `undef` when conditional include order is not provable.
3905#[derive(Clone, Debug, PartialEq, Eq)]
3906pub enum ObjectMacroFieldEvent {
3907    Define {
3908        name: String,
3909        replacement: ObjectMacroReplacement,
3910        conditional: bool,
3911    },
3912    Undef {
3913        name: String,
3914        conditional: bool,
3915    },
3916}
3917
3918/// Collect object-like field-list macros from a parsed source in source order.
3919/// Macros with conflicting active replacements are omitted so an include
3920/// closure cannot silently choose one guarded definition over another.
3921pub fn collect_cpp_object_macro_fields<'tree>(
3922    root: Node<'tree>,
3923    source: &str,
3924) -> HashMap<String, ObjectMacroReplacement> {
3925    let mut fields = HashMap::default();
3926    let mut ambiguous = HashSet::default();
3927    for event in collect_cpp_object_macro_field_events(root, source) {
3928        match event {
3929            ObjectMacroFieldEvent::Define {
3930                name,
3931                replacement: value,
3932                conditional,
3933            } => {
3934                if value.is_empty() {
3935                    fields.remove(&name);
3936                    if conditional {
3937                        ambiguous.insert(name);
3938                    } else {
3939                        ambiguous.remove(&name);
3940                    }
3941                } else if ambiguous.contains(&name) {
3942                    // Keep the name blocked until an explicit undef resets it.
3943                } else if let Some(previous) = fields.get(&name) {
3944                    if previous != &value {
3945                        fields.remove(&name);
3946                        ambiguous.insert(name);
3947                    }
3948                } else {
3949                    fields.insert(name, value);
3950                }
3951            }
3952            ObjectMacroFieldEvent::Undef { name, conditional } => {
3953                fields.remove(&name);
3954                if conditional {
3955                    ambiguous.insert(name);
3956                } else {
3957                    ambiguous.remove(&name);
3958                }
3959            }
3960        }
3961    }
3962    fields
3963}
3964
3965/// Collect object-like field-list macro events in source order. Conditional
3966/// branches remain visible in the event stream: callers that cannot prove
3967/// branch selection can invalidate names conservatively instead of inventing
3968/// a replacement from one branch.
3969pub fn collect_cpp_object_macro_field_events<'tree>(
3970    root: Node<'tree>,
3971    source: &str,
3972) -> Vec<ObjectMacroFieldEvent> {
3973    let mut events = Vec::new();
3974    let mut stack = vec![root];
3975    while let Some(node) = stack.pop() {
3976        if node.kind() == "preproc_def"
3977            && let Some(name) = extract_macro_name(node, source)
3978        {
3979            let replacement = object_macro_replacement_of(node, source);
3980            events.push(ObjectMacroFieldEvent::Define {
3981                name,
3982                replacement,
3983                conditional: inside_preprocessor_conditional(node),
3984            });
3985        } else if is_cpp_undef_directive(node, source)
3986            && let Some(argument) = node.child_by_field_name("argument")
3987        {
3988            events.push(ObjectMacroFieldEvent::Undef {
3989                name: node_text(argument, source).trim().to_string(),
3990                conditional: inside_preprocessor_conditional(node),
3991            });
3992        }
3993        let mut cursor = node.walk();
3994        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3995        stack.extend(children.into_iter().rev());
3996    }
3997    events
3998}
3999
4000fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
4001    let mut current = node.parent();
4002    while let Some(parent) = current {
4003        if matches!(
4004            parent.kind(),
4005            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
4006        ) {
4007            return true;
4008        }
4009        current = parent.parent();
4010    }
4011    false
4012}
4013
4014fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
4015    node.kind() == "preproc_call"
4016        && node
4017            .child_by_field_name("directive")
4018            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
4019}
4020
4021impl<'a> CppVisitor<'a> {
4022    /// Records `code_unit` with the answers the walk carries forward, then adds
4023    /// it to the parse product.
4024    ///
4025    /// Every declaration this walk publishes goes through this family, so the
4026    /// carried-forward answers see each one exactly once: the field ownership
4027    /// index behind [`Self::has_enum_enumerator_units`] (#2786) and the minted
4028    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
4029    fn add_declaration(
4030        &mut self,
4031        code_unit: CodeUnit,
4032        node: Node<'_>,
4033        parent: Option<CodeUnit>,
4034        top_level: Option<CodeUnit>,
4035    ) {
4036        self.note_declaration(&code_unit);
4037        let source = self.source;
4038        self.parsed
4039            .add_code_unit(code_unit, node, source, parent, top_level);
4040    }
4041
4042    /// Range-based form of [`Self::add_declaration`].
4043    fn add_declaration_with_range(
4044        &mut self,
4045        code_unit: CodeUnit,
4046        range: Range,
4047        parent: Option<CodeUnit>,
4048        top_level: Option<CodeUnit>,
4049    ) {
4050        self.note_declaration(&code_unit);
4051        self.parsed
4052            .add_code_unit_with_range(code_unit, range, parent, top_level);
4053    }
4054
4055    /// Deferred-replacement form of [`Self::add_declaration`].
4056    fn replace_declaration_deferred(
4057        &mut self,
4058        code_unit: CodeUnit,
4059        node: Node<'_>,
4060        parent: Option<CodeUnit>,
4061        top_level: Option<CodeUnit>,
4062    ) {
4063        self.note_replaced_declaration(&code_unit);
4064        let source = self.source;
4065        self.parsed
4066            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
4067    }
4068
4069    /// Range-based form of [`Self::replace_declaration_deferred`].
4070    fn replace_declaration_with_range_deferred(
4071        &mut self,
4072        code_unit: CodeUnit,
4073        range: Range,
4074        parent: Option<CodeUnit>,
4075        top_level: Option<CodeUnit>,
4076    ) {
4077        self.note_replaced_declaration(&code_unit);
4078        self.parsed
4079            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
4080    }
4081
4082    /// Notes one declaration about to enter the parse product.
4083    ///
4084    /// A declaration the product already holds is not a creation, so an open
4085    /// recovery capture ignores it -- which is the membership test the set
4086    /// difference it replaces performed. A creation inside a nested recovery
4087    /// belongs to the recoveries around it too, so every open capture takes it.
4088    fn note_declaration(&mut self, code_unit: &CodeUnit) {
4089        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
4090            for capture in &mut self.recovery_captures {
4091                if capture.removed_pre_existing.contains(code_unit) {
4092                    continue;
4093                }
4094                if capture.created_units.insert(code_unit.clone()) {
4095                    capture.created.push(code_unit.clone());
4096                }
4097            }
4098        }
4099        if let Some(field_owners) = self.field_owners.as_mut() {
4100            field_owners.record(code_unit, self.file);
4101        }
4102    }
4103
4104    /// Notes one declaration about to replace an existing one.
4105    ///
4106    /// A deferred replacement of a declaration that already owns children
4107    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
4108    /// a removal is the one thing the field index cannot absorb by addition.
4109    /// Drop it; the next question rebuilds it from the declarations that
4110    /// survive. A replacement of a unit with no children, and a "replacement"
4111    /// of a unit that is not there at all, remove nothing.
4112    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
4113        let removes_children = self.parsed.contains_declaration(code_unit)
4114            && self
4115                .parsed
4116                .children
4117                .get(code_unit)
4118                .is_some_and(|children| !children.is_empty());
4119        if removes_children {
4120            if !self.recovery_captures.is_empty() {
4121                let removed = self.declarations_a_replacement_removes(code_unit);
4122                for capture in &mut self.recovery_captures {
4123                    for unit in &removed {
4124                        // A declaration this capture watched being created is
4125                        // its own; one it did not is a declaration that was
4126                        // already there when the capture opened, so creating it
4127                        // again is a restoration and not a mint.
4128                        if !capture.created_units.contains(unit) {
4129                            capture.removed_pre_existing.insert(unit.clone());
4130                        }
4131                    }
4132                }
4133            }
4134            self.field_owners = None;
4135        }
4136        self.note_declaration(code_unit);
4137    }
4138
4139    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
4140    /// when `code_unit` is replaced: its children, transitively.
4141    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
4142        let mut removed = Vec::new();
4143        let mut seen = HashSet::default();
4144        let mut pending: Vec<CodeUnit> = self
4145            .parsed
4146            .children
4147            .get(code_unit)
4148            .cloned()
4149            .unwrap_or_default();
4150        while let Some(unit) = pending.pop() {
4151            if !seen.insert(unit.clone()) {
4152                continue;
4153            }
4154            if let Some(children) = self.parsed.children.get(&unit) {
4155                pending.extend(children.iter().cloned());
4156            }
4157            removed.push(unit);
4158        }
4159        removed
4160    }
4161
4162    fn visit_function_like_export_class_pair<'tree>(
4163        &mut self,
4164        node: Node<'tree>,
4165        scope: &ScopeInfo,
4166        stack: &mut Vec<CppWork<'tree>>,
4167        ancestry: &ParentIndex<'tree>,
4168    ) -> bool {
4169        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
4170            return false;
4171        };
4172        let member_outcome = self
4173            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
4174        // A malformed class body can escape into several following siblings
4175        // before the next export-macro class head appears. Inspect siblings in
4176        // order and stop at the first envelope that contains such a head. One
4177        // envelope can contain several following classes, all recovered in a
4178        // single bounded traversal.
4179        let mut displaced = node.next_named_sibling();
4180        while let Some(candidate) = displaced {
4181            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
4182                break;
4183            }
4184            displaced = candidate.next_named_sibling();
4185        }
4186        let class_unit = self.visit_named_class_like_shape(
4187            node,
4188            recovered.name,
4189            // The adjacent initializer_list proves the class body envelope,
4190            // but its children are expression-shaped rather than declaration-
4191            // preserving. Index the class identity here; callable definitions
4192            // remain available from their ordinary out-of-line declarations.
4193            None,
4194            true,
4195            Some(recovered.range),
4196            recovered.raw_supertypes,
4197            scope,
4198            stack,
4199            ancestry,
4200        );
4201        self.parsed
4202            .record_materialization(MaterializationRecord::RecoveredDeclaration {
4203                recovery: recovered.range,
4204                unit: class_unit.clone(),
4205            });
4206        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4207            && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4208                tree.root_node(),
4209                class_unit.identifier(),
4210                self.source,
4211            )
4212        {
4213            self.visit_recovered_fragment_constructor(
4214                range,
4215                body,
4216                node,
4217                &class_unit,
4218                scope,
4219                ancestry,
4220            );
4221        }
4222        if let Some(outcome) = member_outcome {
4223            self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4224        }
4225        self.consumed_fragment_regions
4226            .push((node.start_byte(), recovered.range.end_byte));
4227        true
4228    }
4229
4230    fn visit_embedded_function_like_export_classes<'tree>(
4231        &mut self,
4232        node: Node<'tree>,
4233        scope: &ScopeInfo,
4234        stack: &mut Vec<CppWork<'tree>>,
4235        ancestry: &ParentIndex<'tree>,
4236    ) -> bool {
4237        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
4238        let found = !recovered_classes.is_empty();
4239        for recovered in recovered_classes {
4240            let member_outcome = self.reparse_fragmented_export_class_members(
4241                &recovered.fragmented_body,
4242                &recovered.name,
4243            );
4244            let class_unit = self.visit_named_class_like_shape(
4245                node,
4246                recovered.name,
4247                None,
4248                true,
4249                Some(recovered.range),
4250                Some(recovered.raw_supertypes),
4251                scope,
4252                stack,
4253                ancestry,
4254            );
4255            self.parsed
4256                .record_materialization(MaterializationRecord::RecoveredDeclaration {
4257                    recovery: recovered.range,
4258                    unit: class_unit.clone(),
4259                });
4260            if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
4261                && let Some((range, body)) = cpp_reparsed_merged_inline_constructor(
4262                    tree.root_node(),
4263                    class_unit.identifier(),
4264                    self.source,
4265                )
4266            {
4267                self.visit_recovered_fragment_constructor(
4268                    range,
4269                    body,
4270                    node,
4271                    &class_unit,
4272                    scope,
4273                    ancestry,
4274                );
4275            }
4276            if let Some(outcome) = member_outcome {
4277                self.visit_fragmented_export_class_members(outcome, class_unit, scope);
4278            }
4279        }
4280        found
4281    }
4282
4283    /// Walk `node`'s container, answering every ancestor question from
4284    /// `ancestry`.
4285    ///
4286    /// `ancestry` must index the tree `node` belongs to. The caller owns it
4287    /// because one tree can be walked more than once -- a header's C and C++
4288    /// readings are the same tree under different tag semantics -- and the
4289    /// parent relation is a property of the tree, not of the reading.
4290    #[allow(clippy::too_many_arguments)]
4291    pub fn visit_container<'tree>(
4292        &mut self,
4293        node: Node<'tree>,
4294        ancestry: &ParentIndex<'tree>,
4295        package_name: &str,
4296        module: Option<CodeUnit>,
4297        class_unit: Option<CodeUnit>,
4298        template_signature: Option<String>,
4299        visible_using_namespaces: Vec<String>,
4300    ) {
4301        let scope = ScopeInfo {
4302            package_name: package_name.to_string(),
4303            module,
4304            class_unit,
4305            template_signature,
4306            template_metadata: None,
4307            declarations_are_fields: false,
4308            recovered_specialization_member_scope: false,
4309            visible_using_namespaces,
4310        };
4311        // The work loop dispatches a container's children, never the container
4312        // itself, so an `ERROR` container that is the seed does not reach the
4313        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
4314        // breaks the parse leaves the whole translation unit as that seed
4315        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
4316        // macro-error-class recovery reads.
4317        if node.is_error() {
4318            self.visit_object_macro_error_classes(node, &scope);
4319        }
4320        self.run_container_work(node, scope, ancestry);
4321        while let Some((tree, range, scope)) = self.partitioned_regions.pop() {
4322            let root = tree.root_node();
4323            let container = root
4324                .descendant_for_byte_range(range.start, range.end)
4325                .expect("the queued container belongs to this tree");
4326            assert_eq!(container.byte_range(), range);
4327            self.run_container_work(container, scope, &ParentIndex::new(root));
4328        }
4329    }
4330
4331    /// Whether a work node lies entirely inside a byte region consumed by a
4332    /// fragmented export-class recovery (#938); such nodes were already indexed
4333    /// as members of the recovered class by the region reparse.
4334    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
4335        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
4336    }
4337
4338    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
4339    /// candidate a recovery is about to reparse but has not yet turned into a
4340    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
4341    /// prior structured recovery.
4342    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
4343        self.consumed_fragment_regions
4344            .iter()
4345            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
4346    }
4347
4348    /// Drive the container work loop from an explicit seed scope to completion. The
4349    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
4350    /// stays alive for the whole traversal.
4351    ///
4352    /// Every ancestor question this walk asks is answered from `ancestry`, which
4353    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
4354    /// node's position in the tree, which made a generated header with thousands
4355    /// of top-level declarations quadratic (#2361). The index is the caller's
4356    /// because it outlives any one walk: the file's tree is walked twice when a
4357    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
4358    /// builds its own index for its own tree.
4359    fn run_container_work<'tree>(
4360        &mut self,
4361        node: Node<'tree>,
4362        scope: ScopeInfo,
4363        ancestry: &ParentIndex<'tree>,
4364    ) {
4365        self.drain_cpp_work(
4366            vec![CppWork::Container(CppContainer { node, scope })],
4367            ancestry,
4368        );
4369    }
4370
4371    /// The work loop itself, from whatever seed the caller built.
4372    ///
4373    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
4374    /// that must walk only part of a reparsed tree seeds it with the sibling
4375    /// range it may walk instead, which keeps the `using namespace X;` scope
4376    /// accumulation `advance_cpp_siblings` performs.
4377    fn drain_cpp_work<'tree>(
4378        &mut self,
4379        mut stack: Vec<CppWork<'tree>>,
4380        ancestry: &ParentIndex<'tree>,
4381    ) {
4382        while let Some(work) = stack.pop() {
4383            match work {
4384                CppWork::Container(container) => {
4385                    push_cpp_container_work(container.node, container.scope, &mut stack);
4386                }
4387                CppWork::Siblings(siblings) => {
4388                    advance_cpp_siblings(siblings, self.source, &mut stack);
4389                }
4390                CppWork::Node(work) => {
4391                    if self.node_is_inside_consumed_fragment(work.node) {
4392                        continue;
4393                    }
4394                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
4395                }
4396            }
4397        }
4398    }
4399
4400    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
4401    /// it only when the entire region is member-shaped. This validation must happen
4402    /// before registering the recovered class because a rejected speculative range
4403    /// must not leak into the ordinary recovery path.
4404    fn reparse_fragmented_export_class_members(
4405        &self,
4406        fragmented: &FragmentedExportBody,
4407        class_name: &str,
4408    ) -> Option<FragmentedExportMembers> {
4409        if fragmented.reparse_start >= fragmented.reparse_end {
4410            return None;
4411        }
4412        let tree = cpp_reparse_fragmented_class_body(
4413            self.source,
4414            fragmented.reparse_start,
4415            fragmented.reparse_end,
4416        )?;
4417        if cpp_reparsed_members_are_indexable(tree.root_node(), self.source) {
4418            return Some(FragmentedExportMembers::Complete(tree));
4419        }
4420        let has_conditional_constructor = {
4421            let root = tree.root_node();
4422            let mut cursor = root.walk();
4423            root.named_children(&mut cursor).any(|child| {
4424                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
4425            })
4426        };
4427        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
4428    }
4429
4430    /// Index an already validated fragmented body as members of `class_unit`. The
4431    /// region reparse keeps each member's exact original byte and line positions.
4432    fn visit_fragmented_export_class_members(
4433        &mut self,
4434        outcome: FragmentedExportMembers,
4435        class_unit: CodeUnit,
4436        scope: &ScopeInfo,
4437    ) -> bool {
4438        let (tree, complete) = match outcome {
4439            FragmentedExportMembers::Complete(tree) => (tree, true),
4440            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
4441        };
4442        let root = tree.root_node();
4443        let class_name = class_unit.identifier().to_string();
4444        let member_scope = ScopeInfo {
4445            // A recovered export-macro class may borrow its namespace from an
4446            // earlier forward declaration even when the malformed node itself
4447            // sits at file scope. Use the recovered class identity as the
4448            // authoritative package for reparsed members as well.
4449            package_name: class_unit.package_name().to_string(),
4450            module: scope.module.clone(),
4451            class_unit: Some(class_unit),
4452            template_signature: scope.template_signature.clone(),
4453            template_metadata: None,
4454            declarations_are_fields: true,
4455            recovered_specialization_member_scope: false,
4456            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4457        };
4458        if !complete {
4459            // A conditional beginning immediately after an access label can
4460            // fragment one constructor declaration while leaving the rest of
4461            // the class body as unsafe statement soup. Recover only that
4462            // structurally proven constructor and leave the outer-tree
4463            // siblings unconsumed for their ordinary walk.
4464            let mut cursor = root.walk();
4465            let constructors = root
4466                .named_children(&mut cursor)
4467                .filter_map(|child| {
4468                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
4469                })
4470                .collect::<Vec<_>>();
4471            // The reparsed region is its own tree, so this drain walks it with
4472            // its own parent index.
4473            let reparsed_ancestry = ParentIndex::new(root);
4474            for constructor in constructors {
4475                let mut stack = Vec::new();
4476                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
4477                while let Some(work) = stack.pop() {
4478                    match work {
4479                        CppWork::Container(container) => {
4480                            push_cpp_container_work(container.node, container.scope, &mut stack);
4481                        }
4482                        CppWork::Siblings(siblings) => {
4483                            advance_cpp_siblings(siblings, self.source, &mut stack);
4484                        }
4485                        CppWork::Node(work) => {
4486                            self.visit_node(work.node, &work.scope, &mut stack, &reparsed_ancestry)
4487                        }
4488                    }
4489                }
4490            }
4491            return false;
4492        }
4493        // The reparsed region is its own tree, so this walk indexes it itself.
4494        self.run_container_work(root, member_scope, &ParentIndex::new(root));
4495        true
4496    }
4497
4498    fn visit_recovered_fragment_constructor<'tree>(
4499        &mut self,
4500        range: std::ops::Range<usize>,
4501        constructor_body: Node<'tree>,
4502        class_declaration: Node<'tree>,
4503        class_unit: &CodeUnit,
4504        scope: &ScopeInfo,
4505        ancestry: &ParentIndex<'tree>,
4506    ) {
4507        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
4508            return;
4509        };
4510        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
4511            tree.root_node(),
4512            range.start,
4513            class_unit.identifier(),
4514            self.source,
4515        ) else {
4516            return;
4517        };
4518        let member_scope = ScopeInfo {
4519            package_name: class_unit.package_name().to_string(),
4520            module: scope.module.clone(),
4521            class_unit: Some(class_unit.clone()),
4522            template_signature: scope.template_signature.clone(),
4523            template_metadata: None,
4524            declarations_are_fields: true,
4525            recovered_specialization_member_scope: false,
4526            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4527        };
4528        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
4529        else {
4530            return;
4531        };
4532        debug_assert_eq!(function.name, class_unit.identifier());
4533        let code_unit = function.code_unit(self.file.clone());
4534        self.add_declaration_with_range(
4535            code_unit.clone(),
4536            Range {
4537                start_byte: function_declarator.start_byte(),
4538                end_byte: constructor_body.end_byte(),
4539                start_line: function_declarator.start_position().row + 1,
4540                end_line: constructor_body.end_position().row + 1,
4541            },
4542            None,
4543            None,
4544        );
4545        self.parsed.add_signature_with_metadata(
4546            code_unit.clone(),
4547            cpp_signature_metadata(
4548                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
4549                function_declarator,
4550                cpp_callable_linkage(class_declaration, self.source, ancestry),
4551                self.source,
4552                ancestry,
4553            )
4554            .with_declaration_only(false),
4555        );
4556        self.parsed.add_child(class_unit.clone(), code_unit);
4557    }
4558
4559    fn visit_recovered_fragment_prefix_members<'tree>(
4560        &mut self,
4561        root: Node<'tree>,
4562        constructor_start: usize,
4563        class_unit: &CodeUnit,
4564        scope: &ScopeInfo,
4565        ancestry: &ParentIndex<'tree>,
4566    ) {
4567        let member_scope = ScopeInfo {
4568            package_name: class_unit.package_name().to_string(),
4569            module: scope.module.clone(),
4570            class_unit: Some(class_unit.clone()),
4571            template_signature: scope.template_signature.clone(),
4572            template_metadata: None,
4573            declarations_are_fields: true,
4574            recovered_specialization_member_scope: false,
4575            visible_using_namespaces: scope.visible_using_namespaces.clone(),
4576        };
4577        let mut stack = vec![root];
4578        while let Some(current) = stack.pop() {
4579            if current.kind() == "comment" || current.start_byte() >= constructor_start {
4580                continue;
4581            }
4582            if current.end_byte() <= constructor_start
4583                && current.kind() != "translation_unit"
4584                && current.kind() != "labeled_statement"
4585                && current.kind() != "ERROR"
4586            {
4587                let mut work_stack = Vec::new();
4588                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
4589                while let Some(work) = work_stack.pop() {
4590                    match work {
4591                        CppWork::Container(container) => {
4592                            push_cpp_container_work(
4593                                container.node,
4594                                container.scope,
4595                                &mut work_stack,
4596                            );
4597                        }
4598                        CppWork::Siblings(siblings) => {
4599                            advance_cpp_siblings(siblings, self.source, &mut work_stack);
4600                        }
4601                        CppWork::Node(work) => {
4602                            self.visit_node(work.node, &work.scope, &mut work_stack, ancestry)
4603                        }
4604                    }
4605                }
4606                continue;
4607            }
4608            if matches!(
4609                current.kind(),
4610                "translation_unit" | "labeled_statement" | "ERROR"
4611            ) {
4612                let mut cursor = current.walk();
4613                stack.extend(current.named_children(&mut cursor));
4614            }
4615        }
4616    }
4617
4618    fn visit_node<'tree>(
4619        &mut self,
4620        node: Node<'tree>,
4621        scope: &ScopeInfo,
4622        stack: &mut Vec<CppWork<'tree>>,
4623        ancestry: &ParentIndex<'tree>,
4624    ) {
4625        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
4626            self.visit_node(node, &recovered_scope, stack, ancestry);
4627            return;
4628        }
4629        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
4630            self.visit_node(node, &recovered_scope, stack, ancestry);
4631            return;
4632        }
4633        // Fragmented-class recovery below may consume a malformed function
4634        // envelope before the ordinary kind dispatch runs. Recover any
4635        // export-macro class embedded in that envelope first; the strict class
4636        // head/base/body predicate is independent of which later recovery owns
4637        // the surrounding parser fragment.
4638        if node.kind() == "function_definition" && node.has_error() {
4639            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4640        }
4641        if let Some(FragmentedClassRecovery {
4642            declaration_node: class_node,
4643            name,
4644            raw_supertypes,
4645            body: fragmented,
4646        }) = fragmented_class_body(node, self.source)
4647        {
4648            let displaced_namespace_items =
4649                displaced_fragment_namespace_geometry(node, self.source)
4650                    .map(|boundary| boundary.namespace_items)
4651                    .unwrap_or_default();
4652            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
4653            let mut class_stack = Vec::new();
4654            // When the full body cannot be safely reparsed, the original class
4655            // node still proves ownership for its parser-visible prefix.
4656            let parser_visible_body =
4657                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
4658                    .then(|| cpp_body_node(class_node))
4659                    .flatten();
4660            let class_unit = self.visit_named_class_like_shape(
4661                class_node,
4662                name,
4663                parser_visible_body,
4664                true,
4665                Some(fragmented.class_range),
4666                Some(raw_supertypes),
4667                scope,
4668                &mut class_stack,
4669                ancestry,
4670            );
4671            let member_scope = ScopeInfo {
4672                package_name: class_unit.package_name().to_string(),
4673                module: scope.module.clone(),
4674                class_unit: Some(class_unit.clone()),
4675                template_signature: scope.template_signature.clone(),
4676                template_metadata: None,
4677                declarations_are_fields: true,
4678                recovered_specialization_member_scope: false,
4679                visible_using_namespaces: scope.visible_using_namespaces.clone(),
4680            };
4681            let complete = outcome.is_some_and(|outcome| {
4682                self.visit_fragmented_export_class_members(outcome, class_unit, scope)
4683            });
4684            if complete {
4685                self.consumed_fragment_regions
4686                    .push((node.start_byte(), fragmented.class_range.end_byte));
4687            } else {
4688                // A macro-constrained member can make the full body reparse
4689                // unsafe while tree-sitter still exposes later class members
4690                // as bounded siblings up to the displaced `}`/`;`. Keep the
4691                // structurally proven class/base declaration and re-own those
4692                // sibling nodes under it. They retain their original parser
4693                // nodes and exact ranges; the close boundary comes solely from
4694                // `fragmented_class_body`.
4695                // Template wrappers put the escaped members beside the
4696                // template rather than beside its malformed declaration.
4697                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
4698                    if candidate.start_byte() >= fragmented.reparse_end {
4699                        break;
4700                    }
4701                    if cpp_fragment_sibling_is_class_member(
4702                        candidate,
4703                        fragmented.reparse_end,
4704                        self.source,
4705                    ) {
4706                        self.recovered_class_sibling_scopes
4707                            .insert(candidate.id(), member_scope.clone());
4708                    }
4709                }
4710            }
4711            for item in displaced_namespace_items {
4712                self.recovered_class_sibling_scopes
4713                    .insert(item.id(), scope.clone());
4714            }
4715            stack.extend(class_stack);
4716            return;
4717        }
4718        if self.visit_folded_aggregate(node, scope) {
4719            return;
4720        }
4721        match node.kind() {
4722            "template_declaration" => {
4723                if let Some(recovered) =
4724                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
4725                {
4726                    let mut template_scope = scope.clone();
4727                    template_scope.template_signature =
4728                        cpp_template_signature(node, recovered.declaration_node, self.source);
4729                    template_scope.template_metadata =
4730                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
4731                    let raw_supertypes =
4732                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
4733                    let mut class_stack = Vec::new();
4734                    let class_unit = self.visit_named_class_like_shape(
4735                        recovered.class_node,
4736                        recovered.name,
4737                        Some(recovered.body),
4738                        true,
4739                        Some(recovered.range),
4740                        raw_supertypes,
4741                        &template_scope,
4742                        &mut class_stack,
4743                        ancestry,
4744                    );
4745                    self.parsed.record_materialization(
4746                        MaterializationRecord::RecoveredDeclaration {
4747                            recovery: recovered.range,
4748                            unit: class_unit.clone(),
4749                        },
4750                    );
4751                    let member_scope = ScopeInfo {
4752                        package_name: template_scope.package_name.clone(),
4753                        module: template_scope.module.clone(),
4754                        class_unit: Some(class_unit.clone()),
4755                        template_signature: template_scope.template_signature.clone(),
4756                        template_metadata: None,
4757                        declarations_are_fields: true,
4758                        recovered_specialization_member_scope: recovered
4759                            .class_node
4760                            .child_by_field_name("name")
4761                            .is_some_and(|name| name.kind() == "template_type"),
4762                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
4763                    };
4764                    for tail_member in recovered.tail_members.into_iter().rev() {
4765                        stack.push(CppWork::Node(CppNodeWork {
4766                            node: tail_member,
4767                            scope: member_scope.clone(),
4768                        }));
4769                    }
4770                    stack.extend(class_stack);
4771                    for sibling in recovered.member_siblings {
4772                        self.recovered_class_sibling_scopes
4773                            .insert(sibling.id(), member_scope.clone());
4774                    }
4775                    return;
4776                }
4777                for index in (0..node.named_child_count()).rev() {
4778                    let Some(child) = node.named_child(index) else {
4779                        continue;
4780                    };
4781                    if matches!(
4782                        child.kind(),
4783                        "class_specifier"
4784                            | "struct_specifier"
4785                            | "union_specifier"
4786                            | "enum_specifier"
4787                            | "function_definition"
4788                            | "declaration"
4789                            | "field_declaration"
4790                            | "alias_declaration"
4791                            | "namespace_definition"
4792                    ) {
4793                        let mut template_scope = scope.clone();
4794                        template_scope.template_signature =
4795                            cpp_template_signature(node, child, self.source);
4796                        template_scope.template_metadata =
4797                            cpp_template_metadata(node, child, self.source, ancestry);
4798                        if let Some(recovered) = recover_fragmented_partial_specialization(
4799                            node,
4800                            child,
4801                            self.source,
4802                            ancestry,
4803                        ) {
4804                            let code_unit = self.visit_named_class_like_shape(
4805                                recovered.declaration_node,
4806                                recovered.name,
4807                                None,
4808                                true,
4809                                Some(recovered.range),
4810                                None,
4811                                &template_scope,
4812                                stack,
4813                                ancestry,
4814                            );
4815                            self.parsed.record_materialization(
4816                                MaterializationRecord::RecoveredDeclaration {
4817                                    recovery: recovered.range,
4818                                    unit: code_unit.clone(),
4819                                },
4820                            );
4821                            let mut member_scope = template_scope.clone();
4822                            member_scope.class_unit = Some(code_unit);
4823                            member_scope.declarations_are_fields = true;
4824                            member_scope.recovered_specialization_member_scope = true;
4825                            for prefix_member in recovered.prefix_members.into_iter().rev() {
4826                                stack.push(CppWork::Node(CppNodeWork {
4827                                    node: prefix_member,
4828                                    scope: member_scope.clone(),
4829                                }));
4830                            }
4831                            for sibling in recovered.member_siblings {
4832                                self.recovered_class_sibling_scopes
4833                                    .insert(sibling.id(), member_scope.clone());
4834                            }
4835                            for following in recovered.following_declarations.into_iter().rev() {
4836                                stack.push(CppWork::Node(CppNodeWork {
4837                                    node: following,
4838                                    scope: scope.clone(),
4839                                }));
4840                            }
4841                            return;
4842                        }
4843                        stack.push(CppWork::Node(CppNodeWork {
4844                            node: child,
4845                            scope: template_scope,
4846                        }));
4847                    }
4848                }
4849            }
4850            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
4851            "linkage_specification" => {
4852                if let Some(body) = cpp_body_node(node) {
4853                    stack.push(CppWork::Container(CppContainer {
4854                        node: body,
4855                        scope: scope.clone(),
4856                    }));
4857                } else {
4858                    stack.push(CppWork::Container(CppContainer {
4859                        node,
4860                        scope: scope.clone(),
4861                    }));
4862                }
4863            }
4864            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
4865                self.visit_class_like(node, scope, stack, ancestry)
4866            }
4867            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
4868            // A bare namespace-begin sentinel can make tree-sitter promote the
4869            // wrapped declaration to an ERROR node instead of the usual bogus
4870            // function_definition envelope. Keep the recovery entry point on
4871            // the same structured path for both shapes; ordinary ERROR nodes
4872            // retain their declaration-preserving wrapper traversal when the
4873            // sentinel predicate does not match.
4874            "ERROR" => {
4875                self.visit_object_macro_error_classes(node, scope);
4876                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4877                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
4878                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
4879                        return;
4880                    }
4881                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
4882                        return;
4883                    }
4884                    self.visit_macro_swallowed_function_declarations(node, scope);
4885                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
4886                    self.visit_stranded_class_members(node, scope, ancestry);
4887                    stack.push(CppWork::Container(CppContainer {
4888                        node,
4889                        scope: scope.clone(),
4890                    }));
4891                }
4892            }
4893            "declaration" => {
4894                if node.has_error() {
4895                    // Object-like macro lines before a function-like
4896                    // export-macro class head keep the head in a `declaration`
4897                    // rather than in an `ERROR` (#2924). It is the same head
4898                    // and the same following-sibling body, so it takes the same
4899                    // recovery; without it the head's macro invocation indexes
4900                    // as a field named after the macro.
4901                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
4902                        return;
4903                    }
4904                    self.visit_prototype_macro_declarations(node, scope);
4905                    if self.node_is_inside_consumed_fragment(node) {
4906                        // The whole declaration was a recovered K&R
4907                        // prototype macro invocation (issue #2932); the
4908                        // reparse above already indexed its Function.
4909                        // Falling through to the ordinary declaration
4910                        // visitor would additionally mint the swapped-field
4911                        // wreckage this recovery exists to replace.
4912                        return;
4913                    }
4914                }
4915                if scope.class_unit.is_some()
4916                    && scope.declarations_are_fields
4917                    && scope.recovered_specialization_member_scope
4918                    && let Some(alias_name) =
4919                        recovered_using_declaration_alias_name(node, self.source)
4920                {
4921                    self.add_type_aliases(node, scope, vec![alias_name], ancestry);
4922                } else {
4923                    self.visit_declaration(
4924                        node,
4925                        scope,
4926                        scope.declarations_are_fields,
4927                        stack,
4928                        ancestry,
4929                    )
4930                }
4931            }
4932            // A K&R prototype macro invocation with a pointer return type and a
4933            // simple argument list parses with no ERROR node at all: tree-sitter
4934            // reads `T *name _(( args ));` as a multiplication of a type by a
4935            // call (`T * name_(...)`), wrapped in an `expression_statement`
4936            // that `has_error()` only because of a `MISSING "::"` inside it
4937            // (issue #2932). Nothing else mints a declaration from an
4938            // `expression_statement` at declaration scope today, so there is
4939            // no existing behavior to preserve here if this node is not the
4940            // K&R shape; the admission gate inside
4941            // `visit_prototype_macro_declarations` (exactly one clean function
4942            // prototype spanning the recovered run) is what keeps this arm
4943            // safe on an ordinary errorful expression statement that happens
4944            // to contain nested parentheses.
4945            "expression_statement" => {
4946                if node.has_error() {
4947                    self.visit_prototype_macro_declarations(node, scope);
4948                }
4949            }
4950            "field_declaration" => self.visit_declaration(node, scope, true, stack, ancestry),
4951            "preproc_call" => self.visit_preproc_call(node, scope),
4952            "type_definition" | "alias_declaration" => {
4953                self.visit_type_declaration(node, scope, stack, ancestry)
4954            }
4955            "preproc_def" | "preproc_function_def" => self.visit_macro(node),
4956            // `#include` is collected by `collect_cpp_includes` before the
4957            // walk, so a directive the container walk never reaches -- inside
4958            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
4959            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
4960            // include claim.
4961            "preproc_include" => {}
4962            kind if preserves_declaration_scope_through_wrapper(
4963                kind,
4964                scope.class_unit.is_some(),
4965            ) =>
4966            {
4967                // A preprocessor conditional gates every declaration inside it
4968                // on a configuration this analyzer never evaluates; record the
4969                // interval so declaration state can say so (issue #1476). The
4970                // else/elif branches are children of the `preproc_if` node, so
4971                // recording the openers covers every branch.
4972                if kind == "labeled_statement" {
4973                    self.visit_access_label_constructor(node, scope);
4974                }
4975                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
4976                    let mut range = cpp_declaration_range(node);
4977                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
4978                        range.end_byte = boundary.end_byte;
4979                        range.end_line = boundary.end_line;
4980                    }
4981                    self.parsed.record_materialization(
4982                        MaterializationRecord::ConfigurationConditional { range },
4983                    );
4984                    if node.has_error() {
4985                        // A malformed export-macro class can close the namespace
4986                        // node early while the enclosing include guard still owns
4987                        // the remaining class-head/body pairs. The ordinary walk
4988                        // cannot carry the lost namespace through those promoted
4989                        // siblings. Scan only structured ERROR nodes in this
4990                        // already-malformed conditional; the pair recovery's
4991                        // exact class/macro/body predicate remains the admission
4992                        // gate, and its namespace lifting restores the owner.
4993                        let mut candidates = vec![node];
4994                        while let Some(candidate) = candidates.pop() {
4995                            // An `ERROR` still inside a namespace body is one
4996                            // the ordinary walk reaches with that namespace in
4997                            // scope. Claiming it here registers the recovered
4998                            // class at file scope and the consumed region then
4999                            // suppresses the walk that would have named it
5000                            // correctly -- which is why Botan's `DL_Group` was
5001                            // `DL_Group` and not `Botan.DL_Group` in the real
5002                            // header, where an include guard wraps the
5003                            // namespace, and was right in a fixture without one
5004                            // (#2552).
5005                            if candidate.kind() == "ERROR"
5006                                && !cpp_is_inside_namespace_body(candidate, ancestry)
5007                                && self.visit_function_like_export_class_pair(
5008                                    candidate, scope, stack, ancestry,
5009                                )
5010                            {
5011                                continue;
5012                            }
5013                            for index in (0..candidate.named_child_count()).rev() {
5014                                candidates.push(
5015                                    candidate
5016                                        .named_child(index)
5017                                        .expect("index below the node's own named child count"),
5018                                );
5019                            }
5020                        }
5021                    }
5022                }
5023                stack.push(CppWork::Container(CppContainer {
5024                    node,
5025                    scope: scope.clone(),
5026                }))
5027            }
5028            // A macro invocation the parser could not group leaves its head in
5029            // whatever slot was free. The `function_definition` and `ERROR`
5030            // arms above try the same recovery on the envelopes they get; a
5031            // flattened run leaves a bare `identifier`/`type_identifier`, and a
5032            // swallowed tail can land in a `parameter_declaration` (#3094).
5033            _ => {
5034                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
5035            }
5036        }
5037    }
5038
5039    fn visit_macro_swallowed_function_declarations<'tree>(
5040        &mut self,
5041        envelope: Node<'tree>,
5042        scope: &ScopeInfo,
5043    ) {
5044        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
5045            || envelope.kind() == "ERROR"
5046                && envelope
5047                    .parent()
5048                    .is_some_and(|parent| parent.kind() == "ERROR")
5049        {
5050            return;
5051        }
5052        let mut stack = (0..envelope.named_child_count())
5053            .filter_map(|index| envelope.named_child(index))
5054            .collect::<Vec<_>>();
5055        while let Some(node) = stack.pop() {
5056            if node.kind() == "function_declarator" {
5057                self.visit_error_swallowed_function_declaration(node, scope);
5058            }
5059            for child in named_children_iter(node) {
5060                stack.push(child);
5061            }
5062        }
5063    }
5064
5065    /// Index the declarations an attribute-like macro invocation swallowed into
5066    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
5067    /// the shape and why the parser produces it.
5068    fn visit_macro_wrapped_declarations<'tree>(
5069        &mut self,
5070        envelope: Node<'tree>,
5071        scope: &ScopeInfo,
5072        ancestry: &ParentIndex<'tree>,
5073    ) {
5074        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
5075        if recovered.is_empty() {
5076            return;
5077        }
5078        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
5079        self.record_recovered_declarations(recovery, |visitor| {
5080            for declaration in recovered {
5081                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
5082            }
5083        });
5084    }
5085
5086    /// Index the declarations a macro invocation collapsed. See
5087    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
5088    /// produces it. Returns whether it claimed the region beginning at
5089    /// `envelope`.
5090    ///
5091    /// The parser's own nodes cannot be read the way the swallowed tail of the
5092    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
5093    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
5094    /// with no declarator left at all, and the declarators that do survive on
5095    /// the envelope's declarator spine pair one declaration's name with the
5096    /// *next* declaration's parameter list. Reading those would be a guess.
5097    ///
5098    /// The bytes are still ordinary declarations, though, and the collapse is
5099    /// the parser carrying the failure forward from one macro invocation. So
5100    /// reparse the region, walk the items the parser makes of it, and when one
5101    /// item is itself a collapsed run, recover that invocation from its own
5102    /// bytes and resume the scan just past it. Each pass starts later than the
5103    /// last, so the scan is a loop over the invocations that collapse, not over
5104    /// the declarations: `llama.h` needs three passes for 214 declarations.
5105    ///
5106    /// The region can reach past `envelope` into the siblings the parser handed
5107    /// the rest of one invocation, so it is recorded as consumed: those
5108    /// siblings are this recovery's, and the ordinary walk must not read them
5109    /// again. Recording it after the scan, not before, keeps the scan's own
5110    /// reparsed nodes -- which carry their original byte offsets -- visible to
5111    /// the walk it drives.
5112    fn visit_collapsed_macro_declaration_run<'tree>(
5113        &mut self,
5114        envelope: Node<'tree>,
5115        scope: &ScopeInfo,
5116        ancestry: &ParentIndex<'tree>,
5117    ) -> bool {
5118        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
5119            return false;
5120        };
5121        let start = envelope.start_byte();
5122        let end = run.region_end;
5123        let recovery = cpp_recovery_window(self.source, start, end);
5124        self.record_recovered_declarations(recovery, |visitor| {
5125            let mut position = start;
5126            while position < end {
5127                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
5128                    return;
5129                };
5130                let root = tree.root_node();
5131                // A region reparse is its own tree and needs its own parent
5132                // index; the caller's answers nothing about these nodes.
5133                let ancestry = ParentIndex::new(root);
5134                let mut cursor = root.walk();
5135                let collapsed =
5136                    root.named_children(&mut cursor)
5137                        .enumerate()
5138                        .find_map(|(index, item)| {
5139                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
5140                                .map(|run| (index, item, run))
5141                        });
5142                // Walk only the items before the collapsed one. It swallowed
5143                // the rest of the region, so handing it to the ordinary walk
5144                // would re-enter this recovery on a region that starts where
5145                // this one did.
5146                let mut stack = Vec::new();
5147                push_cpp_sibling_range(
5148                    root,
5149                    0,
5150                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
5151                    scope.clone(),
5152                    &mut stack,
5153                );
5154                visitor.drain_cpp_work(stack, &ancestry);
5155                let Some((_, item, run)) = collapsed else {
5156                    return;
5157                };
5158                // On its own bytes the invocation is the declaration-scope
5159                // shape `macro_wrapped_declarations` reads, so its wrapped
5160                // declaration needs no reader of its own here.
5161                if let Some(head) =
5162                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
5163                {
5164                    let head_root = head.root_node();
5165                    visitor.run_container_work(
5166                        head_root,
5167                        scope.clone(),
5168                        &ParentIndex::new(head_root),
5169                    );
5170                }
5171                assert!(
5172                    run.invocation_end > position,
5173                    "a collapsed run at {position} must end after the byte the scan resumed \
5174                     from, but ended at {}",
5175                    run.invocation_end
5176                );
5177                position = run.invocation_end;
5178            }
5179        });
5180        self.consumed_fragment_regions.push((start, end));
5181        true
5182    }
5183
5184    /// Index the members a string-argument attribute macro stranded in an
5185    /// `ERROR` inside a class body.
5186    ///
5187    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
5188    /// grouping of the attributed member and of every member written after it
5189    /// until it recovers. The members are still whole `function_declarator`
5190    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
5191    /// export-macro class such as Botan's `DL_Group` was indexed with no
5192    /// members at all (#2552).
5193    fn visit_stranded_class_members<'tree>(
5194        &mut self,
5195        node: Node<'tree>,
5196        scope: &ScopeInfo,
5197        ancestry: &ParentIndex<'tree>,
5198    ) {
5199        if scope.class_unit.is_none() || !scope.declarations_are_fields {
5200            return;
5201        }
5202        for member in stranded_declaration_run(node, self.source).declarations {
5203            self.add_macro_wrapped_declaration(member, scope, ancestry);
5204        }
5205    }
5206
5207    /// Index the constructor an access label swallowed in a reparsed class
5208    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
5209    ///
5210    /// The declarator is recovered by reparsing from that call to the end of
5211    /// the label's own statement, which is the same offset-preserving region
5212    /// reparse every other recovery here uses, so the recovered nodes carry
5213    /// their true source positions.
5214    fn visit_access_label_constructor(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5215        let Some(class_unit) = scope.class_unit.clone() else {
5216            return;
5217        };
5218        if !scope.declarations_are_fields {
5219            return;
5220        }
5221        let class_name = class_unit.identifier().to_string();
5222        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
5223        else {
5224            return;
5225        };
5226        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
5227            return;
5228        };
5229        let root = tree.root_node();
5230        let Some(declarator) =
5231            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
5232        else {
5233            return;
5234        };
5235        let reparsed_ancestry = ParentIndex::new(root);
5236        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
5237        let range = cpp_declaration_range(definition.unwrap_or(declarator));
5238        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
5239        self.record_recovered_declarations(recovery, |visitor| {
5240            visitor.add_macro_wrapped_declaration(
5241                MacroWrappedDeclaration {
5242                    declarator,
5243                    range,
5244                    is_static: false,
5245                },
5246                scope,
5247                &reparsed_ancestry,
5248            );
5249        });
5250    }
5251
5252    fn add_macro_wrapped_declaration<'tree>(
5253        &mut self,
5254        declaration: MacroWrappedDeclaration<'tree>,
5255        scope: &ScopeInfo,
5256        ancestry: &ParentIndex<'tree>,
5257    ) {
5258        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
5259        else {
5260            return;
5261        };
5262        let code_unit =
5263            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
5264        if self.parsed.contains_declaration(&code_unit) {
5265            self.parsed
5266                .record_navigation_range(code_unit, declaration.range);
5267            return;
5268        }
5269        self.add_declaration_with_range(code_unit.clone(), declaration.range, None, None);
5270        let signature = normalize_cpp_whitespace(
5271            self.source
5272                .get(declaration.range.start_byte..declaration.range.end_byte)
5273                .expect("a recovered declaration range covers one source range"),
5274        );
5275        let linkage = if declaration.is_static {
5276            CallableLinkage::Internal
5277        } else {
5278            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
5279        };
5280        // Whether this is a definition is a property of the recovered node, not
5281        // of the caller: a declarator that a `function_definition` gives a body
5282        // is a definition wherever the recovery found it.
5283        let declaration_only =
5284            cpp_declarator_function_definition(declaration.declarator, ancestry).is_none();
5285        self.parsed.add_signature_with_metadata(
5286            code_unit.clone(),
5287            cpp_signature_metadata(
5288                signature,
5289                declaration.declarator,
5290                linkage,
5291                self.source,
5292                ancestry,
5293            )
5294            .with_declaration_only(declaration_only),
5295        );
5296        if let Some(parent) = &scope.class_unit {
5297            self.parsed.add_child(parent.clone(), code_unit);
5298        } else if let Some(module) = &scope.module {
5299            self.parsed.add_child(module.clone(), code_unit);
5300        }
5301    }
5302
5303    /// Index only anonymous aggregate declarations from an ordinary
5304    /// function body. Function bodies are otherwise outside the declaration
5305    /// walk, but their anonymous aggregate owners must exist so structured
5306    /// local binding inference can name their fields (#2994).
5307    fn visit_anonymous_local_aggregates_in_function<'tree>(
5308        &mut self,
5309        function: Node<'tree>,
5310        scope: &ScopeInfo,
5311        stack: &mut Vec<CppWork<'tree>>,
5312        ancestry: &ParentIndex<'tree>,
5313    ) {
5314        let Some(body) = cpp_body_node(function) else {
5315            return;
5316        };
5317        // A body opens a block scope, so nothing declared in it is a member of
5318        // the class the definition may be written inside. Dropping the
5319        // aggregate carrier gives an inline member function's body the same
5320        // block scope an out-of-line `void C::f()` body already has; without
5321        // it a C++ header, where member bodies are usually inline, would keep
5322        // none of these owners (#3301).
5323        let block_scope;
5324        let scope = if scope.class_unit.is_some() {
5325            block_scope = ScopeInfo {
5326                class_unit: None,
5327                declarations_are_fields: false,
5328                ..scope.clone()
5329            };
5330            &block_scope
5331        } else {
5332            scope
5333        };
5334        let mut pending = vec![body];
5335        while let Some(node) = pending.pop() {
5336            if matches!(node.kind(), "function_definition" | "lambda_expression") {
5337                continue;
5338            }
5339            if node.kind() == "declaration"
5340                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
5341            {
5342                continue;
5343            }
5344            if matches!(
5345                node.kind(),
5346                "class_specifier" | "struct_specifier" | "union_specifier"
5347            ) {
5348                continue;
5349            }
5350            let mut cursor = node.walk();
5351            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
5352            children.reverse();
5353            pending.extend(children);
5354        }
5355    }
5356
5357    fn visit_error_swallowed_function_declaration<'tree>(
5358        &mut self,
5359        node: Node<'tree>,
5360        scope: &ScopeInfo,
5361    ) -> bool {
5362        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
5363            return false;
5364        };
5365        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
5366            return false;
5367        };
5368        let root = tree.root_node();
5369        let mut cursor = root.walk();
5370        let declarations = root
5371            .named_children(&mut cursor)
5372            .filter(|child| child.kind() != "comment")
5373            .collect::<Vec<_>>();
5374        let [declaration] = declarations.as_slice() else {
5375            return false;
5376        };
5377        if declaration.kind() != "declaration"
5378            || declaration.has_error()
5379            || declaration.start_byte() != start
5380            || declaration.end_byte() != end
5381        {
5382            return false;
5383        }
5384        let recovery = cpp_recovery_window(self.source, start, end);
5385        // The reparsed region is its own tree, so this walk indexes it itself.
5386        let reparsed_ancestry = ParentIndex::new(root);
5387        self.record_recovered_declarations(recovery, |visitor| {
5388            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5389        });
5390        true
5391    }
5392
5393    /// Recover a K&R-compatibility prototype macro invocation such as
5394    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
5395    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
5396    /// defined only in headers outside this workspace (ruby's own
5397    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
5398    /// `name _((args))` and turns each such line into a malformed
5399    /// `declaration` or pointer-expression statement. Before this
5400    /// recovery, the ordinary declaration visitor read that wreckage as a
5401    /// field with the type and name swapped (e.g. a field literally named
5402    /// `VALUE`) and indexed no prototype for the real name at all.
5403    ///
5404    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
5405    /// preserve the declared name, a known one-argument compatibility macro,
5406    /// the nested argument list, and a live terminator. It rejects arbitrary
5407    /// identifiers and bare `ERROR` envelopes that flattened those relations;
5408    /// guessing there previously turned malformed non-macro declarations into
5409    /// invented functions. For each proven candidate this reparses the range
5410    /// before the macro token, the inner parenthesized argument node, and the
5411    /// terminating `;` as one included-range parse. That is the parser-native
5412    /// equivalent of expanding `#define _(args) args` while retaining original
5413    /// byte offsets. The result is admitted only when it is exactly one clean
5414    /// function prototype spanning the recovered run.
5415    fn visit_prototype_macro_declarations(&mut self, node: Node<'_>, scope: &ScopeInfo) {
5416        for candidate in cpp_prototype_macro_candidates(node, self.source) {
5417            let start = candidate.run_start;
5418            let end = candidate.semicolon_end;
5419            if self.byte_range_is_inside_consumed_fragment(start, end) {
5420                continue;
5421            }
5422            let Some(tree) = parse_source_ranges_with_cancellation(
5423                &tree_sitter_cpp::LANGUAGE.into(),
5424                self.source,
5425                &candidate.ranges(),
5426                None,
5427            ) else {
5428                continue;
5429            };
5430            let root = tree.root_node();
5431            let mut cursor = root.walk();
5432            let declarations = root
5433                .named_children(&mut cursor)
5434                .filter(|child| child.kind() != "comment")
5435                .collect::<Vec<_>>();
5436            let [declaration] = declarations.as_slice() else {
5437                continue;
5438            };
5439            if declaration.kind() != "declaration"
5440                || declaration.has_error()
5441                || declaration.start_byte() != start
5442                || declaration.end_byte() != end
5443                || declaration
5444                    .child_by_field_name("declarator")
5445                    .and_then(extract_function_declarator)
5446                    .is_none()
5447            {
5448                continue;
5449            }
5450            let recovery = cpp_recovery_window(self.source, start, end);
5451            // The reparsed region is its own tree, so this walk indexes it
5452            // itself.
5453            let reparsed_ancestry = ParentIndex::new(root);
5454            self.record_recovered_declarations(recovery, |visitor| {
5455                visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
5456            });
5457            self.consumed_fragment_regions.push((start, end));
5458        }
5459    }
5460
5461    /// Declare one Module per namespace level of `levels` under `package_name`,
5462    /// each recording the range of the construct that writes it, and return the
5463    /// innermost level's package name and Module. A level an earlier definition
5464    /// already declared is reused, which is also how a reopened namespace keeps
5465    /// the range of the definition that first declared it.
5466    fn declare_namespace_levels(
5467        &mut self,
5468        mut package_name: String,
5469        levels: Vec<(String, Range)>,
5470    ) -> (String, Option<CodeUnit>) {
5471        let mut module = None;
5472        for (component, range) in levels {
5473            let full_name = if package_name.is_empty() {
5474                component
5475            } else {
5476                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
5477            };
5478            let level = CodeUnit::new_fq(
5479                self.file.clone(),
5480                CodeUnitType::Module,
5481                "",
5482                full_name.clone(),
5483                cpp_namespace_fq(&full_name),
5484            );
5485            if !self.parsed.contains_declaration(&level) {
5486                self.add_declaration_with_range(level.clone(), range, None, None);
5487            }
5488            package_name = full_name;
5489            module = Some(level);
5490        }
5491        (package_name, module)
5492    }
5493
5494    /// Restore the complete lexical namespace path when recovery dropped an
5495    /// ancestor or retained it past its real close. Parsed namespaces beginning
5496    /// inside the recovered region still contribute their normal nesting.
5497    /// Crossing that boundary also clears any malformed class owner.
5498    fn recovered_namespace_scope(
5499        &mut self,
5500        node: Node<'_>,
5501        scope: &ScopeInfo,
5502    ) -> Option<ScopeInfo> {
5503        self.orphaned_namespaces.region_at(node.start_byte())?;
5504        // Each level carries the `namespace Name { ... }` construct that writes
5505        // it. Declaring them at `node` instead recorded the range of whichever
5506        // member the walk happened to reach first, so a namespace whose head
5507        // collapsed into an ERROR reported its first inner declaration's lines
5508        // as its own (#3309).
5509        let levels = self
5510            .orphaned_namespaces
5511            .enclosing_namespace_levels(node, self.source);
5512        let package_name = levels
5513            .iter()
5514            .map(|(component, _)| component.as_str())
5515            .collect::<Vec<_>>()
5516            .join(CPP_PACKAGE_SEPARATOR);
5517        if package_name == scope.package_name {
5518            return None;
5519        }
5520        let (package_name, module) = self.declare_namespace_levels(String::new(), levels);
5521        Some(ScopeInfo {
5522            package_name,
5523            module,
5524            // A recovered namespace body is no longer inside the class (or
5525            // function) that tree-sitter accidentally used as its wrapper.
5526            // Keep namespace/module context, but never carry that owner into
5527            // declarations published from the recovered region.
5528            class_unit: None,
5529            template_signature: None,
5530            template_metadata: None,
5531            declarations_are_fields: false,
5532            recovered_specialization_member_scope: false,
5533            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5534        })
5535    }
5536
5537    fn visit_namespace<'tree>(
5538        &mut self,
5539        node: Node<'tree>,
5540        scope: &ScopeInfo,
5541        stack: &mut Vec<CppWork<'tree>>,
5542        ancestry: &ParentIndex<'tree>,
5543    ) {
5544        let name_node = node.child_by_field_name("name");
5545        let Some(name_node) = name_node else {
5546            if let Some(body) = cpp_body_node(node) {
5547                stack.push(CppWork::Container(CppContainer {
5548                    node: body,
5549                    scope: scope.clone(),
5550                }));
5551            }
5552            return;
5553        };
5554        // Diagnostic corpora contain deliberately ill-formed global namespace
5555        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
5556        // the leading global `::` as the first anonymous child. Honor that AST
5557        // boundary instead of appending the name to the lexical namespace;
5558        // appending produced legacy names such as `outer::::outer::inner`, which
5559        // could not round-trip through the structured FqName boundary.
5560        let explicitly_global = name_node
5561            .child(0)
5562            .is_some_and(|child| !child.is_named() && child.kind() == "::");
5563        let components = cpp_namespace_name_components(name_node, self.source);
5564        if components.is_empty() {
5565            return;
5566        }
5567        // One Module per namespace level. C++17's `namespace a::b { ... }` is
5568        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
5569        // shorthand must declare `a` as well as `a::b` -- extracting only the
5570        // innermost level left the enclosing namespace undeclared and made the
5571        // two spellings of one construct disagree (issue #1878).
5572        let package_name = if explicitly_global {
5573            String::new()
5574        } else {
5575            scope.package_name.clone()
5576        };
5577        let definition = node_range(node);
5578        let (package_name, module) = self.declare_namespace_levels(
5579            package_name,
5580            components
5581                .into_iter()
5582                .map(|component| (component, definition))
5583                .collect(),
5584        );
5585
5586        let namespace_scope = ScopeInfo {
5587            package_name,
5588            module,
5589            // C++ never nests a namespace inside a class, so a surviving
5590            // class_unit here is always recovery bleed: a malformed-region
5591            // boundary upstream mis-scoped this namespace block. Keeping the
5592            // owner would mint the namespace's declarations as class members
5593            // under a re-appended package, desyncing the fq boundary assert
5594            // (#2306). Dropping it is identity-neutral for valid code, where
5595            // class_unit is always empty at a namespace definition.
5596            class_unit: None,
5597            template_signature: scope.template_signature.clone(),
5598            template_metadata: scope.template_metadata.clone(),
5599            declarations_are_fields: false,
5600            recovered_specialization_member_scope: false,
5601            visible_using_namespaces: scope.visible_using_namespaces.clone(),
5602        };
5603        let container = cpp_body_node(node).unwrap_or(node);
5604        // A malformed export-macro class body may turn the following class
5605        // into a descendant of a bogus function/labeled/error envelope. Those
5606        // descendants are not declaration containers and the ordinary walk
5607        // intentionally does not descend into them. Scan the namespace tree
5608        // once for the strict embedded class geometry before scheduling its
5609        // normal declarations. When one envelope matches, its helper recovers
5610        // every embedded class and the walk need not inspect its descendants.
5611        let mut candidates = vec![container];
5612        while let Some(candidate) = candidates.pop() {
5613            if matches!(
5614                candidate.kind(),
5615                "ERROR" | "function_definition" | "labeled_statement"
5616            ) && self.visit_embedded_function_like_export_classes(
5617                candidate,
5618                &namespace_scope,
5619                stack,
5620                ancestry,
5621            ) {
5622                continue;
5623            }
5624            for index in (0..candidate.named_child_count()).rev() {
5625                candidates.push(
5626                    candidate
5627                        .named_child(index)
5628                        .expect("index below the node's own named child count"),
5629                );
5630            }
5631        }
5632        stack.push(CppWork::Container(CppContainer {
5633            node: container,
5634            scope: namespace_scope,
5635        }));
5636    }
5637
5638    fn visit_class_like<'tree>(
5639        &mut self,
5640        node: Node<'tree>,
5641        scope: &ScopeInfo,
5642        stack: &mut Vec<CppWork<'tree>>,
5643        ancestry: &ParentIndex<'tree>,
5644    ) {
5645        let Some(name) = class_like_name(node, self.source, ancestry) else {
5646            return;
5647        };
5648        let name = qualified_class_name_chain(node, self.source, scope)
5649            .map(|chain| chain.join("$"))
5650            .unwrap_or(name);
5651        self.visit_named_class_like(node, name, scope, stack, ancestry);
5652    }
5653
5654    fn visit_named_class_like<'tree>(
5655        &mut self,
5656        node: Node<'tree>,
5657        name: String,
5658        scope: &ScopeInfo,
5659        stack: &mut Vec<CppWork<'tree>>,
5660        ancestry: &ParentIndex<'tree>,
5661    ) {
5662        let body = cpp_body_node(node);
5663        let definition_body_present = body.is_some();
5664        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
5665            .then(|| extract_cpp_supertypes(node, self.source));
5666        self.visit_named_class_like_shape(
5667            node,
5668            name,
5669            body,
5670            definition_body_present,
5671            None,
5672            raw_supertypes,
5673            scope,
5674            stack,
5675            ancestry,
5676        );
5677    }
5678
5679    /// Whether this class-like declaration is a C tag that belongs to the
5680    /// enclosing non-aggregate scope rather than to the aggregate it is
5681    /// lexically written inside.
5682    ///
5683    /// `class_specifier` is deliberately excluded: `class` is not C, so text
5684    /// that spells one in a `.c` file is not C code and keeps the C++ reading
5685    /// rather than getting a half-C identity.
5686    fn mints_tag_at_enclosing_c_scope(
5687        &self,
5688        declaration_node: Node<'_>,
5689        scope: &ScopeInfo,
5690        ancestry: &ParentIndex<'_>,
5691    ) -> bool {
5692        scope.class_unit.is_some()
5693            && matches!(
5694                declaration_node.kind(),
5695                "struct_specifier" | "union_specifier" | "enum_specifier"
5696            )
5697            && class_like_name(declaration_node, self.source, ancestry).is_some()
5698    }
5699
5700    #[allow(clippy::too_many_arguments)]
5701    fn visit_named_class_like_shape<'tree>(
5702        &mut self,
5703        declaration_node: Node<'tree>,
5704        name: String,
5705        body: Option<Node<'tree>>,
5706        definition_body_present: bool,
5707        explicit_range: Option<Range>,
5708        raw_supertypes: Option<Vec<String>>,
5709        scope: &ScopeInfo,
5710        stack: &mut Vec<CppWork<'tree>>,
5711        ancestry: &ParentIndex<'tree>,
5712    ) -> CodeUnit {
5713        let displaced_macro_tail = if explicit_range.is_none() {
5714            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
5715        } else {
5716            None
5717        };
5718        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
5719        let recovered_scope = self.scope_for_recovered_exported_class(
5720            declaration_node,
5721            &name,
5722            definition_body_present,
5723            scope,
5724            ancestry,
5725        );
5726        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
5727        // aggregate's member list is declared at the enclosing non-aggregate
5728        // scope, not nested inside the aggregate. `scope.class_unit` is the
5729        // only aggregate carrier in this walk, so dropping it puts the tag at
5730        // the nearest enclosing non-aggregate scope -- the module at file or
5731        // namespace scope, and the same block-scope representation a
5732        // function-local aggregate already gets. The tag's own body scope
5733        // below still owns its members, so fields and enumerators are
5734        // unaffected.
5735        let c_tag_scope;
5736        let c_tag_scope_applies =
5737            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
5738        if !self.c_tag_semantics && c_tag_scope_applies {
5739            self.c_tag_scope_witness = true;
5740        }
5741        let scope = if self.c_tag_semantics && c_tag_scope_applies {
5742            c_tag_scope = ScopeInfo {
5743                class_unit: None,
5744                ..recovered_scope.clone()
5745            };
5746            &c_tag_scope
5747        } else {
5748            &recovered_scope
5749        };
5750        let short_name = if let Some(parent) = &scope.class_unit {
5751            cpp_join_nested_short(parent.short_name(), &name)
5752        } else {
5753            name.clone()
5754        };
5755        // A top-level out-of-line qualified class definition (`struct
5756        // Outer::Inner { ... }` inside its namespace, #2246) carries its
5757        // nesting chain as the `$`-joined display name; push one Type/Nested
5758        // segment per class so segment-pop owner navigation keeps working.
5759        // Every other leaf name stays opaque so a literal `$` in a source
5760        // identifier never crosses the split/join boundary (#2140).
5761        let qualified_chain = if scope.class_unit.is_none() {
5762            qualified_class_name_chain(declaration_node, self.source, scope)
5763                .filter(|chain| chain.join("$") == name)
5764        } else {
5765            None
5766        };
5767        let fq = if let Some(chain) = qualified_chain {
5768            let mut fq = FqName::new();
5769            cpp_push_package(&mut fq, &scope.package_name);
5770            let mut first = true;
5771            for component in chain {
5772                let kind = if first {
5773                    SegmentKind::Type
5774                } else {
5775                    SegmentKind::Nested
5776                };
5777                fq.push(cpp_segment(&component, kind));
5778                first = false;
5779            }
5780            fq
5781        } else {
5782            cpp_leaf_fq(
5783                &scope.package_name,
5784                scope.class_unit.as_ref(),
5785                &name,
5786                SegmentKind::Nested,
5787                SegmentKind::Type,
5788            )
5789        };
5790        let code_unit = CodeUnit::with_signature_and_fq(
5791            self.file.clone(),
5792            CodeUnitType::Class,
5793            scope.package_name.clone(),
5794            short_name,
5795            scope.template_signature.clone(),
5796            false,
5797            fq,
5798        );
5799        let has_body = definition_body_present;
5800        if !has_body && self.parsed.contains_declaration(&code_unit) {
5801            self.parsed.record_navigation_range(
5802                code_unit.clone(),
5803                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
5804            );
5805            return code_unit;
5806        }
5807        if has_body {
5808            // A forward declaration above this definition is one of the
5809            // class's declaration sites: C++ injects the class name at the
5810            // forward declaration, so a reference between the two spells this
5811            // class (#3297). Moving the unit to its body must not drop the
5812            // earlier site, exactly as a function prototype and its body stay
5813            // two ranges of one declaration (#1650).
5814            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
5815            if let Some(range) = explicit_range {
5816                self.replace_declaration_with_range_deferred(code_unit.clone(), range, None, None);
5817            } else {
5818                self.replace_declaration_deferred(code_unit.clone(), declaration_node, None, None);
5819            }
5820            for range in earlier_ranges {
5821                self.parsed.add_declaration_range(&code_unit, range);
5822            }
5823        } else {
5824            self.add_declaration(code_unit.clone(), declaration_node, None, None);
5825        }
5826        if let Some(raw_supertypes) = raw_supertypes {
5827            self.parsed
5828                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
5829        }
5830        self.parsed.add_signature(
5831            code_unit.clone(),
5832            render_cpp_type_signature(
5833                declaration_node,
5834                self.source,
5835                scope.template_signature.as_deref(),
5836            ),
5837        );
5838        if let Some(metadata) = &scope.template_metadata {
5839            let primary_short_name = if let Some(parent) = &scope.class_unit {
5840                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
5841            } else {
5842                metadata.primary_name.clone()
5843            };
5844            let primary_fq_name = CodeUnit::new(
5845                self.file.clone(),
5846                CodeUnitType::Class,
5847                scope.package_name.clone(),
5848                primary_short_name,
5849            )
5850            .fq_name();
5851            let mut metadata = metadata.clone();
5852            metadata.primary_fq_name = primary_fq_name;
5853            self.parsed
5854                .set_cpp_template_metadata(code_unit.clone(), metadata);
5855        }
5856        if let Some(parent) = &scope.class_unit {
5857            self.parsed.add_child(parent.clone(), code_unit.clone());
5858        } else if let Some(module) = &scope.module {
5859            self.parsed.add_child(module.clone(), code_unit.clone());
5860        }
5861
5862        if let Some(body) = body {
5863            let mut nested_scope = scope.clone();
5864            nested_scope.class_unit = Some(code_unit.clone());
5865            nested_scope.template_signature = scope.template_signature.clone();
5866            // Template metadata describes the class just created. It must not
5867            // leak into ordinary nested declarations in that class's body.
5868            // Recovered export-macro specializations carry a separate scope bit
5869            // for their declaration-shaped body members.
5870            nested_scope.template_metadata = None;
5871            // Export-macro class bodies recovered from a function_definition use
5872            // compound_statement children, whose direct fields are declarations.
5873            nested_scope.recovered_specialization_member_scope =
5874                scope.template_metadata.as_ref().is_some_and(|metadata| {
5875                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
5876                });
5877            nested_scope.declarations_are_fields =
5878                is_recovered_exported_class_container(declaration_node, self.source)
5879                    || nested_scope.recovered_specialization_member_scope;
5880            if let Some(displaced) = displaced_macro_tail {
5881                // A macro-shaped field without a source semicolon can make
5882                // tree-sitter consume the real class terminator as an ERROR
5883                // inside that field, then retain following namespace items as
5884                // later field-list children. Drain the proven class prefix
5885                // first and re-own only the structured tail with the outer
5886                // scope. The tail is pushed first because the work stack is
5887                // LIFO.
5888                push_cpp_sibling_range(
5889                    body,
5890                    displaced.split_index,
5891                    usize::MAX,
5892                    scope.clone(),
5893                    stack,
5894                );
5895                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
5896            } else {
5897                stack.push(CppWork::Container(CppContainer {
5898                    node: body,
5899                    scope: nested_scope,
5900                }));
5901            }
5902        }
5903        if declaration_node.kind() == "enum_specifier" {
5904            self.visit_enum_enumerators(declaration_node, scope, &code_unit);
5905            if !self.has_enum_enumerator_units(&code_unit) {
5906                self.visit_enum_enumerators_from_text(declaration_node, scope, &code_unit);
5907            }
5908        }
5909        code_unit
5910    }
5911
5912    /// Whether the parse product already holds enumerator fields for `parent`,
5913    /// answered from the walk's carried-forward field ownership index.
5914    ///
5915    /// Built on the first enum's question and advanced by every declaration
5916    /// recorded after it, so a file that declares no enum -- most files -- pays
5917    /// nothing, and one that declares thousands pays a single pass instead of
5918    /// one per enum (#2786).
5919    fn has_enum_enumerator_units(&mut self, parent: &CodeUnit) -> bool {
5920        if self.field_owners.is_none() {
5921            self.field_owners = Some(CppFieldOwnerIndex::of(
5922                self.parsed.declarations().iter(),
5923                self.file,
5924            ));
5925        }
5926        debug_assert_eq!(
5927            parent.source(),
5928            self.file,
5929            "the walk's declarations are declarations of the file it is walking"
5930        );
5931        let carried = self
5932            .field_owners
5933            .as_ref()
5934            .expect("the index was just ensured")
5935            .owns_fields(parent.package_name(), parent.short_name());
5936
5937        #[cfg(debug_assertions)]
5938        assert_eq!(
5939            carried,
5940            cpp_declarations_hold_owned_fields(
5941                self.parsed.declarations(),
5942                self.file,
5943                parent.package_name(),
5944                parent.short_name()
5945            ),
5946            "the carried-forward field index must answer what a fresh declaration scan \
5947             answers for {}",
5948            parent.fq_name()
5949        );
5950
5951        carried
5952    }
5953
5954    fn visit_enum_enumerators(&mut self, node: Node<'_>, scope: &ScopeInfo, parent: &CodeUnit) {
5955        walk_named_tree_preorder(node, false, |child| {
5956            if child.kind() != "enumerator" {
5957                return WalkControl::Continue;
5958            }
5959            let Some(name_node) = child.child_by_field_name("name") else {
5960                return WalkControl::Continue;
5961            };
5962            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
5963            if name.is_empty() {
5964                return WalkControl::Continue;
5965            }
5966            let code_unit = CodeUnit::new_fq(
5967                self.file.clone(),
5968                CodeUnitType::Field,
5969                scope.package_name.clone(),
5970                cpp_join_member_short(parent.short_name(), &name),
5971                parent
5972                    .fq()
5973                    .clone()
5974                    .with_pushed(cpp_segment(&name, SegmentKind::Member)),
5975            );
5976            if self.parsed.contains_declaration(&code_unit) {
5977                return WalkControl::Continue;
5978            }
5979            self.add_declaration(code_unit.clone(), child, Some(parent.clone()), None);
5980            self.parsed.add_signature(
5981                code_unit,
5982                normalize_cpp_whitespace(node_text(child, self.source)),
5983            );
5984            WalkControl::Continue
5985        });
5986    }
5987
5988    fn visit_enum_enumerators_from_text(
5989        &mut self,
5990        node: Node<'_>,
5991        scope: &ScopeInfo,
5992        parent: &CodeUnit,
5993    ) {
5994        let text = node_text(node, self.source);
5995        let Some((_, body)) = text.split_once('{') else {
5996            return;
5997        };
5998        let Some((body, _)) = body.rsplit_once('}') else {
5999            return;
6000        };
6001        for entry in body.split(',') {
6002            let trimmed = entry.trim();
6003            let name = trimmed
6004                .split('=')
6005                .next()
6006                .unwrap_or("")
6007                .split_whitespace()
6008                .next()
6009                .unwrap_or("");
6010            if name.is_empty() {
6011                continue;
6012            }
6013            let code_unit = CodeUnit::new_fq(
6014                self.file.clone(),
6015                CodeUnitType::Field,
6016                scope.package_name.clone(),
6017                cpp_join_member_short(parent.short_name(), name),
6018                parent
6019                    .fq()
6020                    .clone()
6021                    .with_pushed(cpp_segment(name, SegmentKind::Member)),
6022            );
6023            if self.parsed.contains_declaration(&code_unit) {
6024                continue;
6025            }
6026            self.add_declaration(code_unit.clone(), node, Some(parent.clone()), None);
6027            self.parsed.add_signature(code_unit, trimmed.to_string());
6028        }
6029    }
6030
6031    fn visit_function_definition<'tree>(
6032        &mut self,
6033        node: Node<'tree>,
6034        scope: &ScopeInfo,
6035        stack: &mut Vec<CppWork<'tree>>,
6036        ancestry: &ParentIndex<'tree>,
6037    ) {
6038        // An attribute-like macro invocation whose argument is a declaration can
6039        // swallow every declaration written after it into one bogus
6040        // `function_definition` (#2551). This owns the whole region, so it runs
6041        // ahead of `visit_macro_swallowed_function_declarations` below, which
6042        // admits the same envelope by its head macro token and reads single
6043        // declarators out of nodes this recovery reparses properly.
6044        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6045            return;
6046        }
6047        // A file-scope object-like macro sentinel the parser cannot see (issue
6048        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
6049        // prefixes as a bogus `function_definition` that swallows real namespaces,
6050        // classes, and members. Reparse the swallowed interior as C++ items so the
6051        // ordinary declaration visitors index it with byte/line-exact ownership.
6052        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6053            return;
6054        }
6055        if node.has_error() {
6056            self.visit_macro_swallowed_function_declarations(node, scope);
6057        }
6058        if let Some((class_node, name, raw_supertypes)) =
6059            recover_exported_class_function_definition(node, self.source)
6060        {
6061            if let Some(body) = cpp_body_node(node)
6062                && let Some(close) = self
6063                    .orphaned_namespaces
6064                    .matching_close_brace(body.start_byte())
6065                && close.end_byte < body.end_byte()
6066            {
6067                let class_range = Range {
6068                    start_byte: node.start_byte(),
6069                    end_byte: close.end_byte,
6070                    start_line: node.start_position().row + 1,
6071                    end_line: close.end_line,
6072                };
6073                // Parse the body in class context, preserving the original
6074                // keyword, recovered name and body as included ranges. Parsing
6075                // the interior at file scope turns constructors and access
6076                // labels into another oversized ERROR instead of members.
6077                let mut head = vec![node];
6078                let mut keyword = None;
6079                let mut name_node = None;
6080                while let Some(part) = head.pop() {
6081                    if part.start_byte() >= body.start_byte() || part.is_missing() {
6082                        continue;
6083                    }
6084                    if matches!(part.kind(), "class" | "struct" | "union") {
6085                        keyword = Some(part);
6086                    }
6087                    if matches!(part.kind(), "identifier" | "type_identifier")
6088                        && node_text(part, self.source) == name
6089                    {
6090                        name_node = Some(part);
6091                    }
6092                    let mut cursor = part.walk();
6093                    head.extend(part.children(&mut cursor));
6094                }
6095                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
6096                    && let Some(type_name) = keyword
6097                        .parent()
6098                        .and_then(|parent| parent.child_by_field_name("name"))
6099                    && let Some(tree) = parse_source_ranges_with_cancellation(
6100                        &tree_sitter_cpp::LANGUAGE.into(),
6101                        self.source,
6102                        &[
6103                            (keyword.start_byte(), type_name.start_byte()),
6104                            (name_node.start_byte(), name_node.end_byte()),
6105                            (body.start_byte(), close.end_byte),
6106                        ],
6107                        None,
6108                    )
6109                    && let Some(reparsed_class) = tree.root_node().named_child(0)
6110                    && let Some(class_body) = cpp_body_node(reparsed_class)
6111                    && class_body.start_byte() == body.start_byte()
6112                    && class_body.end_byte() == close.end_byte
6113                    && let Some(tail) =
6114                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
6115                {
6116                    let class_unit = self.visit_named_class_like_shape(
6117                        class_node,
6118                        name,
6119                        None,
6120                        true,
6121                        Some(class_range),
6122                        raw_supertypes,
6123                        scope,
6124                        stack,
6125                        ancestry,
6126                    );
6127                    self.parsed.record_materialization(
6128                        MaterializationRecord::RecoveredDeclaration {
6129                            recovery: class_range,
6130                            unit: class_unit.clone(),
6131                        },
6132                    );
6133                    let member_scope = ScopeInfo {
6134                        package_name: class_unit.package_name().to_string(),
6135                        class_unit: Some(class_unit),
6136                        declarations_are_fields: true,
6137                        template_metadata: None,
6138                        recovered_specialization_member_scope: false,
6139                        ..scope.clone()
6140                    };
6141                    let class_body_range = class_body.byte_range();
6142                    let tail_range = tail.root_node().byte_range();
6143                    self.partitioned_regions
6144                        .push((tail, tail_range, scope.clone()));
6145                    self.partitioned_regions
6146                        .push((tree, class_body_range, member_scope));
6147                    return;
6148                }
6149            }
6150            let body = cpp_body_node(class_node);
6151            let displaced_namespace = cpp_body_node(node)
6152                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
6153            let fragmented = cpp_body_node(node).and_then(|body| {
6154                fragmented_export_function_body_region(
6155                    node,
6156                    body,
6157                    self.source,
6158                    displaced_namespace.as_ref(),
6159                )
6160            });
6161            // The recovery tuple's first node is the class-like type when the
6162            // parser exposes one, but the synthetic wrapper owns the compound
6163            // statement that contains the truncated class body. Use the
6164            // wrapper body for fragmented-member detection; retain the
6165            // class-node body for the ordinary (non-fragmented) path below.
6166            if let Some(fragmented) = fragmented {
6167                // The lifted sibling no longer sits below the parser-visible
6168                // namespace node. Restore the current parent scope when the
6169                // ordinary work walk reaches that class.
6170                if let Some(boundary) =
6171                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
6172                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
6173                {
6174                    let mut boundary_scope = scope.clone();
6175                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
6176                        if sibling.start_byte() >= boundary.start_byte() {
6177                            break;
6178                        }
6179                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
6180                            boundary_scope.visible_using_namespaces.push(namespace);
6181                        }
6182                    }
6183                    self.recovered_class_sibling_scopes
6184                        .insert(boundary.id(), boundary_scope);
6185                }
6186                let mut recovered_constructor = None;
6187                let mut recovered_prefix_tree = None;
6188                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
6189                {
6190                    Some(FragmentedExportMembers::Complete(tree)) => {
6191                        if let Some(body) = body
6192                            && let Some(range) =
6193                                cpp_reparsed_synthetic_initializer_constructor_range(
6194                                    tree.root_node(),
6195                                    &name,
6196                                    self.source,
6197                                    body.end_byte(),
6198                                )
6199                        {
6200                            recovered_constructor = Some(range);
6201                            recovered_prefix_tree = Some(tree);
6202                            None
6203                        } else {
6204                            Some(FragmentedExportMembers::Complete(tree))
6205                        }
6206                    }
6207                    outcome => outcome,
6208                };
6209                let mut class_stack = Vec::new();
6210                let class_unit = self.visit_named_class_like_shape(
6211                    class_node,
6212                    name,
6213                    None,
6214                    true,
6215                    Some(fragmented.class_range),
6216                    raw_supertypes,
6217                    scope,
6218                    &mut class_stack,
6219                    ancestry,
6220                );
6221                self.parsed
6222                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6223                        recovery: fragmented.class_range,
6224                        unit: class_unit.clone(),
6225                    });
6226                let complete = outcome.is_some_and(|outcome| {
6227                    self.visit_fragmented_export_class_members(outcome, class_unit.clone(), scope)
6228                });
6229                if complete {
6230                    self.consumed_fragment_regions
6231                        .push((node.start_byte(), fragmented.class_range.end_byte));
6232                } else {
6233                    // The reparse can fail when the first constructor or a
6234                    // method body is split into statement-shaped siblings.
6235                    // Keep the recovered class envelope, but do not visit the
6236                    // synthetic wrapper body: its initializer expressions can
6237                    // look like same-named member functions (for example
6238                    // `Token.location(loc)`). Re-own only the original sibling
6239                    // nodes that fall inside the proven class range. Their CST
6240                    // shapes retain the real field/function kinds and ranges.
6241                    let member_scope = ScopeInfo {
6242                        package_name: class_unit.package_name().to_string(),
6243                        module: scope.module.clone(),
6244                        class_unit: Some(class_unit.clone()),
6245                        template_signature: scope.template_signature.clone(),
6246                        template_metadata: None,
6247                        declarations_are_fields: true,
6248                        recovered_specialization_member_scope: false,
6249                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
6250                    };
6251                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6252                        if candidate.start_byte() >= fragmented.reparse_end {
6253                            break;
6254                        }
6255                        if cpp_fragment_sibling_is_class_member(
6256                            candidate,
6257                            fragmented.reparse_end,
6258                            self.source,
6259                        ) {
6260                            self.recovered_class_sibling_scopes
6261                                .insert(candidate.id(), member_scope.clone());
6262                        }
6263                    }
6264                    if let Some(range) = recovered_constructor
6265                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
6266                    {
6267                        self.visit_recovered_fragment_prefix_members(
6268                            prefix_tree.root_node(),
6269                            range.start,
6270                            &class_unit,
6271                            scope,
6272                            ancestry,
6273                        );
6274                        self.visit_recovered_fragment_constructor(
6275                            range,
6276                            body,
6277                            class_node,
6278                            &class_unit,
6279                            scope,
6280                            ancestry,
6281                        );
6282                    }
6283                }
6284                if let Some(boundary) = displaced_namespace {
6285                    for item in boundary.namespace_items {
6286                        self.recovered_class_sibling_scopes
6287                            .insert(item.id(), scope.clone());
6288                    }
6289                }
6290                stack.extend(class_stack);
6291                return;
6292            }
6293            let mut stack = Vec::new();
6294            let class_unit = self.visit_named_class_like_shape(
6295                class_node,
6296                name,
6297                body,
6298                body.is_some(),
6299                None,
6300                raw_supertypes,
6301                scope,
6302                &mut stack,
6303                ancestry,
6304            );
6305            self.parsed
6306                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6307                    recovery: cpp_declaration_range(node),
6308                    unit: class_unit,
6309                });
6310            // Issue #1524: the bogus `function_definition` body can run past
6311            // the class's true closing brace (the parse ends it with a
6312            // zero-width `MISSING "}"`), swallowing following namespace-scope
6313            // siblings -- they would index as members of the recovered class.
6314            // When the body's text-balanced close lands before the body's own
6315            // end, re-own the swallowed tail with the outer scope instead.
6316            if let Some(body) = body
6317                && let Some(class_close) = self
6318                    .orphaned_namespaces
6319                    .matching_close_brace(body.start_byte())
6320                && class_close.start_byte < body.end_byte()
6321            {
6322                let split = {
6323                    let mut cursor = body.walk();
6324                    body.named_children(&mut cursor)
6325                        .position(|child| child.start_byte() > class_close.start_byte)
6326                };
6327                if let Some(split) = split {
6328                    // The seeded work is a single Container over the whole
6329                    // body with the class scope; replace it with the bounded
6330                    // head (class scope) plus the swallowed tail (outer
6331                    // scope). Push tail first so the head drains first.
6332                    let seeded = stack.pop();
6333                    match seeded {
6334                        Some(CppWork::Container(container)) => {
6335                            push_cpp_sibling_range(
6336                                body,
6337                                split,
6338                                usize::MAX,
6339                                scope.clone(),
6340                                &mut stack,
6341                            );
6342                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
6343                        }
6344                        // visit_named_class_like_shape always seeds exactly
6345                        // one Container when a body is present.
6346                        _ => unreachable!("exported-class seed is always one Container"),
6347                    }
6348                }
6349            }
6350            while let Some(work) = stack.pop() {
6351                match work {
6352                    CppWork::Container(container) => {
6353                        push_cpp_container_work(container.node, container.scope, &mut stack);
6354                    }
6355                    CppWork::Siblings(siblings) => {
6356                        advance_cpp_siblings(siblings, self.source, &mut stack);
6357                    }
6358                    CppWork::Node(work) => {
6359                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
6360                    }
6361                }
6362            }
6363            return;
6364        }
6365        let recovered_constraint_constructor =
6366            cpp_recovered_template_macro_constructor(node, self.source);
6367        let declarator = recovered_constraint_constructor
6368            .map(|(declarator, _)| declarator)
6369            .or_else(|| node.child_by_field_name("declarator"));
6370        let Some(declarator) = declarator else {
6371            self.visit_malformed_function_definition_container(node, scope, stack);
6372            return;
6373        };
6374        let Some(function_declarator) = extract_function_declarator(declarator) else {
6375            self.visit_malformed_function_definition_container(node, scope, stack);
6376            return;
6377        };
6378        let function = if let Some((_, callable_name)) =
6379            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
6380        {
6381            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
6382        } else {
6383            extract_function_info(function_declarator, self.source, scope)
6384        };
6385        let Some(mut function) = function else {
6386            self.visit_malformed_function_definition_container(node, scope, stack);
6387            return;
6388        };
6389        if let Some((_, template_parameter)) = recovered_constraint_constructor {
6390            function.signature = format!(
6391                "template <{}>{}",
6392                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
6393                function.signature
6394            );
6395        }
6396        let code_unit = function.code_unit(self.file.clone());
6397        // Keep an earlier same-file prototype as another physical occurrence
6398        // of this callable. `CodeUnit` already identifies the role-neutral
6399        // overload, while ranges and signature metadata describe its
6400        // declaration/definition occurrences.
6401        self.add_declaration(code_unit.clone(), node, None, None);
6402        let signature = if recovered_constraint_constructor.is_some() {
6403            normalize_cpp_whitespace(node_text(function_declarator, self.source))
6404        } else {
6405            render_cpp_function_display_signature_from_node(
6406                node,
6407                self.source,
6408                scope.template_signature.as_deref(),
6409                true,
6410                ancestry,
6411            )
6412        };
6413        self.parsed.add_signature_with_metadata(
6414            code_unit.clone(),
6415            cpp_signature_metadata(
6416                signature,
6417                function_declarator,
6418                cpp_callable_linkage(node, self.source, ancestry),
6419                self.source,
6420                ancestry,
6421            )
6422            .with_declaration_only(false),
6423        );
6424        if let Some(parent) = &scope.class_unit {
6425            self.parsed.add_child(parent.clone(), code_unit);
6426        } else if let Some(module) = &scope.module {
6427            self.parsed.add_child(module.clone(), code_unit);
6428        }
6429        self.visit_anonymous_local_aggregates_in_function(node, scope, stack, ancestry);
6430    }
6431
6432    /// Recover the namespace lost when tree-sitter promotes an export-macro
6433    /// class definition to a root-level `function_definition`.  Only a
6434    /// body-bearing, top-level recovery may borrow a namespace, and only when
6435    /// one earlier namespace-scope forward declaration proves the identity.
6436    fn scope_for_recovered_exported_class<'tree>(
6437        &mut self,
6438        node: Node<'tree>,
6439        name: &str,
6440        definition_body_present: bool,
6441        scope: &ScopeInfo,
6442        ancestry: &ParentIndex<'tree>,
6443    ) -> ScopeInfo {
6444        if !definition_body_present
6445            || !scope.package_name.is_empty()
6446            || scope.class_unit.is_some()
6447            || !(is_recovered_exported_class_container(node, self.source)
6448                || recover_function_like_export_class_pair(node, self.source).is_some()
6449                || recover_embedded_function_like_export_classes(node, self.source)
6450                    .iter()
6451                    .any(|recovered| recovered.name == name)
6452                || matches!(node.kind(), "declaration" | "field_declaration")
6453                    && recover_exported_class_declaration(node, self.source).is_some()
6454                || matches!(
6455                    node.kind(),
6456                    "class_specifier" | "struct_specifier" | "union_specifier"
6457                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
6458                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
6459                        name_node,
6460                        self.source,
6461                    )))
6462                }) || ancestry.parent(node).is_some_and(|parent| {
6463                    matches!(parent.kind(), "declaration" | "field_declaration")
6464                        && recover_exported_class_declaration(parent, self.source).is_some()
6465                        || is_recovered_exported_class_container(parent, self.source)
6466                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
6467        {
6468            return scope.clone();
6469        }
6470        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
6471        let Some(package_name) = borrowed_namespace
6472            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
6473        else {
6474            return scope.clone();
6475        };
6476
6477        let module = CodeUnit::new_fq(
6478            self.file.clone(),
6479            CodeUnitType::Module,
6480            "",
6481            package_name.clone(),
6482            cpp_namespace_fq(&package_name),
6483        );
6484        let mut recovered = scope.clone();
6485        recovered.package_name = package_name;
6486        recovered.module = Some(module);
6487        recovered
6488    }
6489
6490    /// The unique namespace-scope forward declaration of `name` that precedes
6491    /// `recovered_node`, answered from the walk's carried-forward scan of the
6492    /// tree `recovered_node` belongs to.
6493    ///
6494    /// The scan is built on the first question and advanced by each later one,
6495    /// so a file that never reaches this path -- almost every file -- pays
6496    /// nothing, and one that reaches it thousands of times pays a single pass
6497    /// (#2754).
6498    fn unique_earlier_namespace_forward<'tree>(
6499        &mut self,
6500        recovered_node: Node<'tree>,
6501        name: &str,
6502        ancestry: &ParentIndex<'tree>,
6503    ) -> Option<String> {
6504        let mut root = recovered_node;
6505        while let Some(parent) = ancestry.parent(root) {
6506            root = parent;
6507        }
6508        let source = self.source;
6509        let scan = self
6510            .namespace_forward_scans
6511            .entry(CppTreeIdentity::of(root))
6512            .or_default();
6513        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
6514        let borrowed = scan.unique_earlier_forward(name, recovered_node);
6515
6516        #[cfg(debug_assertions)]
6517        assert_eq!(
6518            borrowed,
6519            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
6520            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
6521             for {name} at byte {}",
6522            recovered_node.start_byte()
6523        );
6524
6525        borrowed
6526    }
6527
6528    fn visit_malformed_function_definition_container<'tree>(
6529        &mut self,
6530        node: Node<'tree>,
6531        scope: &ScopeInfo,
6532        stack: &mut Vec<CppWork<'tree>>,
6533    ) {
6534        let Some(body) = cpp_body_node(node) else {
6535            return;
6536        };
6537        if !cpp_contains_namespace_definition(body) {
6538            return;
6539        }
6540        stack.push(CppWork::Container(CppContainer {
6541            node: body,
6542            scope: scope.clone(),
6543        }));
6544    }
6545
6546    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
6547    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
6548    /// emits for a sentinel-prefixed region, reparse the interior after the
6549    /// sentinel identifier as real C++ items -- confined to the region so
6550    /// every reparsed node keeps its original byte/line position -- and run the
6551    /// ordinary container visitation over the result. Returns `true` when it fired
6552    /// (the caller must then skip normal function processing). Nested sentinel
6553    /// regions recover recursively: the reparsed interior is walked through the
6554    /// same `visit_function_definition` path, so a sentinel inside the region hits
6555    /// this recovery again.
6556    /// Runs `reparse_walk` and records every declaration it mints as a
6557    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
6558    /// `recovery` (issue #1657). A reparsed sentinel region has no single
6559    /// recovered envelope unit: the ordinary visitors mint namespaces,
6560    /// classes, and members directly from the reparsed tree, so the walk's
6561    /// declaration delta is the recovered set. Records are ordered by
6562    /// declaration start byte so the parse product stays deterministic.
6563    fn record_recovered_declarations(
6564        &mut self,
6565        recovery: Range,
6566        reparse_walk: impl FnOnce(&mut Self),
6567    ) {
6568        // The set difference this used to be, kept as the oracle every answer
6569        // is asserted against (#2787).
6570        #[cfg(any(debug_assertions, test))]
6571        let before = self.parsed.declarations().clone();
6572
6573        self.recovery_captures.push(CppRecoveryCapture::default());
6574        reparse_walk(self);
6575        let captured = self
6576            .recovery_captures
6577            .pop()
6578            .expect("the capture this call pushed is the one it pops");
6579
6580        // The capture holds every declaration created while it was open, once
6581        // each and in creation order, so the recovered set costs what the
6582        // recovery made rather than everything the file has declared so far.
6583        // One filter is left to apply: a created declaration that a later
6584        // deferred replacement removed is not in the parse product to report.
6585        let mut minted: Vec<CodeUnit> = captured
6586            .created
6587            .into_iter()
6588            .filter(|unit| self.parsed.contains_declaration(unit))
6589            .collect();
6590        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6591
6592        #[cfg(any(debug_assertions, test))]
6593        {
6594            let mut rediscovered: Vec<CodeUnit> = self
6595                .parsed
6596                .declarations()
6597                .iter()
6598                .filter(|unit| !before.contains(*unit))
6599                .cloned()
6600                .collect();
6601            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
6602            assert_eq!(
6603                minted, rediscovered,
6604                "the captured recovered set must be the declaration delta of the reparse \
6605                 walk over {recovery:?}"
6606            );
6607        }
6608
6609        for unit in minted {
6610            self.parsed
6611                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6612                    recovery,
6613                    unit,
6614                });
6615        }
6616    }
6617
6618    /// Where one recovered declaration sorts: by start byte, then by name, so
6619    /// the parse product stays deterministic.
6620    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
6621        let start = self
6622            .parsed
6623            .declaration_ranges(unit)
6624            .first()
6625            .map(|range| range.start_byte)
6626            .unwrap_or(usize::MAX);
6627        (start, unit.fq_name().to_string())
6628    }
6629
6630    fn visit_sentinel_macro_region<'tree>(
6631        &mut self,
6632        node: Node<'tree>,
6633        scope: &ScopeInfo,
6634        stack: &mut Vec<CppWork<'tree>>,
6635        ancestry: &ParentIndex<'tree>,
6636    ) -> bool {
6637        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
6638            return true;
6639        }
6640        if let Some((
6641            reparse_start,
6642            class_start,
6643            body_start,
6644            class_close_start,
6645            class_close_end,
6646            class_close_line,
6647        )) = cpp_sentinel_macro_class_region(node, self.source)
6648        {
6649            let Some(class_tree) =
6650                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
6651            else {
6652                return false;
6653            };
6654            let class_root = class_tree.root_node();
6655            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
6656            // A region reparse is its own tree and needs its own parent index.
6657            let class_ancestry = ParentIndex::new(class_root);
6658            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
6659                class_root,
6660                template_node,
6661                self.source,
6662                &class_ancestry,
6663            ) else {
6664                return false;
6665            };
6666            let class_node = reparsed_class.declaration_node;
6667            let name = reparsed_class.name;
6668            let mut class_scope = scope.clone();
6669            if let Some(template_node) = template_node {
6670                class_scope.template_signature =
6671                    cpp_template_signature(template_node, class_node, self.source);
6672                class_scope.template_metadata =
6673                    cpp_template_metadata(template_node, class_node, self.source, ancestry);
6674            }
6675            let Some(body_tree) =
6676                cpp_reparse_region_items(self.source, body_start, class_close_start)
6677            else {
6678                return false;
6679            };
6680            let raw_supertypes = reparsed_class.raw_supertypes;
6681            let class_range = Range {
6682                start_byte: class_start,
6683                end_byte: class_close_end,
6684                start_line: class_node.start_position().row + 1,
6685                end_line: class_close_line,
6686            };
6687            let class_scope = self.scope_for_recovered_exported_class(
6688                class_node,
6689                &name,
6690                true,
6691                &class_scope,
6692                ancestry,
6693            );
6694            let mut class_stack = Vec::new();
6695            let class_unit = self.visit_named_class_like_shape(
6696                class_node,
6697                name,
6698                None,
6699                true,
6700                Some(class_range),
6701                raw_supertypes,
6702                &class_scope,
6703                &mut class_stack,
6704                ancestry,
6705            );
6706            self.parsed
6707                .record_materialization(MaterializationRecord::RecoveredDeclaration {
6708                    recovery: class_range,
6709                    unit: class_unit.clone(),
6710                });
6711            let member_scope = ScopeInfo {
6712                package_name: class_scope.package_name.clone(),
6713                module: class_scope.module.clone(),
6714                class_unit: Some(class_unit),
6715                template_signature: class_scope.template_signature.clone(),
6716                template_metadata: None,
6717                declarations_are_fields: true,
6718                recovered_specialization_member_scope: false,
6719                visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
6720            };
6721            // The padded body reparse is its own tree, so it indexes itself.
6722            let body_root = body_tree.root_node();
6723            self.run_container_work(body_root, member_scope, &ParentIndex::new(body_root));
6724            // Register only after the padded body reparse: its nodes deliberately
6725            // retain offsets inside the consumed region and must be visited first.
6726            self.consumed_fragment_regions
6727                .push((node.start_byte(), class_close_end));
6728            // An ERROR envelope can hold real sibling declarations after the
6729            // recovered class's close (the suffix-reparse boundary in
6730            // `cpp_sentinel_macro_class_region` partitions, it does not
6731            // consume). Walk the envelope's remaining children normally; the
6732            // consumed region above keeps the recovered class from being
6733            // indexed twice.
6734            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
6735                stack.push(CppWork::Container(CppContainer {
6736                    node,
6737                    scope: scope.clone(),
6738                }));
6739            }
6740            return true;
6741        }
6742        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
6743            return false;
6744        };
6745        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
6746            return false;
6747        };
6748        let root = tree.root_node();
6749        if !cpp_reparsed_items_are_indexable(root, self.source) {
6750            return false;
6751        }
6752        let recovery = cpp_recovery_window(self.source, start, end);
6753        // The reparsed region is its own tree, so this walk indexes it itself.
6754        let reparsed_ancestry = ParentIndex::new(root);
6755        self.record_recovered_declarations(recovery, |visitor| {
6756            visitor.visit_container(
6757                root,
6758                &reparsed_ancestry,
6759                &scope.package_name,
6760                scope.module.clone(),
6761                scope.class_unit.clone(),
6762                scope.template_signature.clone(),
6763                scope.visible_using_namespaces.clone(),
6764            );
6765        });
6766        if end > node.end_byte() {
6767            self.consumed_fragment_regions
6768                .push((node.start_byte(), end));
6769        } else if node.kind() == "ERROR" && node.end_byte() > end {
6770            // The sentinel region ended at the first recovered class-like item
6771            // but the ERROR envelope keeps real sibling declarations after it
6772            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
6773            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
6774            // envelope's remaining children normally; the consumed region
6775            // keeps the reparsed prefix from being indexed twice.
6776            self.consumed_fragment_regions
6777                .push((node.start_byte(), end));
6778            stack.push(CppWork::Container(CppContainer {
6779                node,
6780                scope: scope.clone(),
6781            }));
6782        }
6783        true
6784    }
6785
6786    /// Re-own complete class declarations from the structured Abseil
6787    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
6788    /// its direct CST children already prove both namespace components and the
6789    /// class bodies, so the ordinary class/member visitor can retain ownership
6790    /// and exact source ranges without admitting unrelated callable bodies.
6791    fn visit_nested_namespace_sentinel<'tree>(
6792        &mut self,
6793        node: Node<'tree>,
6794        scope: &ScopeInfo,
6795        ancestry: &ParentIndex<'tree>,
6796    ) -> bool {
6797        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
6798            return false;
6799        };
6800
6801        let mut package_name = scope.package_name.clone();
6802        let mut module = scope.module.clone();
6803        for component in recovered.namespace_components {
6804            package_name = if package_name.is_empty() {
6805                component
6806            } else {
6807                format!("{package_name}::{component}")
6808            };
6809            let namespace_module = CodeUnit::new_fq(
6810                self.file.clone(),
6811                CodeUnitType::Module,
6812                "",
6813                package_name.clone(),
6814                cpp_namespace_fq(&package_name),
6815            );
6816            if !self.parsed.contains_declaration(&namespace_module) {
6817                self.add_declaration(namespace_module.clone(), recovered.function, None, None);
6818            }
6819            module = Some(namespace_module);
6820        }
6821
6822        let recovered_scope = ScopeInfo {
6823            package_name,
6824            module,
6825            // This scope's package comes from the namespace levels the sentinel
6826            // shape proves, so its owner must come from the same place. C++
6827            // never nests a namespace inside a class, so a surviving
6828            // `class_unit` here is recovery bleed from an earlier malformed
6829            // region, and keeping it would publish declarations whose package
6830            // names one lexical position and whose owner names another -- the
6831            // hybrid identity that trips the package/short boundary assert in
6832            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
6833            // identity-neutral for valid code, where `class_unit` is always
6834            // empty at a namespace.
6835            class_unit: None,
6836            template_signature: scope.template_signature.clone(),
6837            template_metadata: scope.template_metadata.clone(),
6838            declarations_are_fields: false,
6839            recovered_specialization_member_scope: false,
6840            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6841        };
6842        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
6843            recovered.function,
6844            recovered.body,
6845            self.source,
6846            ancestry,
6847        ) {
6848            let mut class_scope = recovered_scope.clone();
6849            if let Some(template_node) = fragmented.template_node {
6850                class_scope.template_signature =
6851                    cpp_template_signature(template_node, fragmented.class_node, self.source);
6852                class_scope.template_metadata = cpp_template_metadata(
6853                    template_node,
6854                    fragmented.class_node,
6855                    self.source,
6856                    ancestry,
6857                );
6858            }
6859            if let Some(outcome) = self
6860                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
6861            {
6862                let mut class_stack = Vec::new();
6863                let class_unit = self.visit_named_class_like_shape(
6864                    fragmented.class_node,
6865                    fragmented.name.clone(),
6866                    None,
6867                    true,
6868                    Some(fragmented.fragmented.class_range),
6869                    fragmented.raw_supertypes.clone(),
6870                    &class_scope,
6871                    &mut class_stack,
6872                    ancestry,
6873                );
6874                self.parsed
6875                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
6876                        recovery: fragmented.fragmented.class_range,
6877                        unit: class_unit.clone(),
6878                    });
6879                if self.visit_fragmented_export_class_members(outcome, class_unit, &class_scope) {
6880                    self.consumed_fragment_regions.push((
6881                        fragmented.consumed_start,
6882                        fragmented.fragmented.class_range.end_byte,
6883                    ));
6884                }
6885            }
6886        }
6887        // The class requirement above is the admission gate; once admitted,
6888        // traverse the whole proven inner namespace body so sibling aliases,
6889        // functions, and variables are not silently discarded. The body is a
6890        // node of the tree being walked, so it reuses that tree's index.
6891        self.run_container_work(recovered.body, recovered_scope, ancestry);
6892        true
6893    }
6894
6895    fn visit_declaration<'tree>(
6896        &mut self,
6897        node: Node<'tree>,
6898        scope: &ScopeInfo,
6899        in_class_body: bool,
6900        stack: &mut Vec<CppWork<'tree>>,
6901        ancestry: &ParentIndex<'tree>,
6902    ) {
6903        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6904            return;
6905        }
6906        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
6907            return;
6908        }
6909        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
6910            !cpp_active_template_type_parameter(
6911                node,
6912                node_text(declarator, self.source),
6913                self.source,
6914                ancestry,
6915            )
6916        }) {
6917            return;
6918        }
6919        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
6920            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
6921            // the following `Imaging image` member into one malformed field.
6922            // The ERROR's identifier is the actual declarator; route it
6923            // through the ordinary field path so its parent, range, and
6924            // signature metadata remain consistent with every other member.
6925            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
6926            return;
6927        }
6928        if in_class_body
6929            && let Some(parent) = scope.class_unit.as_ref()
6930            && let Some(call) =
6931                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
6932        {
6933            self.visit_recovered_macro_qualified_constructor_definition(
6934                node, call, scope, ancestry,
6935            );
6936            return;
6937        }
6938        if in_class_body
6939            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
6940        {
6941            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
6942            return;
6943        }
6944        if in_class_body
6945            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
6946        {
6947            for member in members {
6948                self.add_macro_wrapped_declaration(member, scope, ancestry);
6949            }
6950            return;
6951        }
6952        if in_class_body
6953            && let Some(declarators) =
6954                recovered_macro_qualified_field_declarators(node, self.source)
6955        {
6956            for declarator in declarators {
6957                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
6958            }
6959            return;
6960        }
6961        let recovered_alias_names = recovered_type_alias_names(node, self.source);
6962        if !recovered_alias_names.is_empty() {
6963            self.add_type_aliases(node, scope, recovered_alias_names, ancestry);
6964            return;
6965        }
6966        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
6967            return;
6968        }
6969        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
6970            return;
6971        }
6972
6973        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
6974            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
6975                // Issue #938: the members tree-sitter scattered out of the fragmented
6976                // multiple-base export node are reparsed from their true body region
6977                // and re-owned as members of the recovered class, with an explicit
6978                // navigation range spanning to the displaced closing brace.
6979                if let Some(outcome) =
6980                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
6981                {
6982                    let consumed_region = (
6983                        recovered.declaration_node.end_byte(),
6984                        fragmented.class_range.end_byte,
6985                    );
6986                    let code_unit = self.visit_named_class_like_shape(
6987                        recovered.declaration_node,
6988                        recovered.name,
6989                        None,
6990                        true,
6991                        Some(fragmented.class_range),
6992                        recovered.raw_supertypes,
6993                        scope,
6994                        stack,
6995                        ancestry,
6996                    );
6997                    self.parsed.record_materialization(
6998                        MaterializationRecord::RecoveredDeclaration {
6999                            recovery: fragmented.class_range,
7000                            unit: code_unit.clone(),
7001                        },
7002                    );
7003                    let consume_fragment =
7004                        self.visit_fragmented_export_class_members(outcome, code_unit, scope);
7005                    // Everything between the fragmented declaration and its displaced
7006                    // closing brace now belongs to the recovered class; keep the
7007                    // ordinary walk from re-indexing those scattered siblings at top
7008                    // level. Register the consumed region only after indexing because
7009                    // the reparsed nodes retain byte offsets inside that same region.
7010                    if consume_fragment {
7011                        self.consumed_fragment_regions.push(consumed_region);
7012                    }
7013                    return;
7014                }
7015            }
7016            let uses_initializer_body = recovered.uses_initializer_body;
7017            let definition_body_present = recovered.body.is_some();
7018            let class_unit = self.visit_named_class_like_shape(
7019                recovered.declaration_node,
7020                recovered.name,
7021                recovered.body,
7022                definition_body_present,
7023                None,
7024                recovered.raw_supertypes,
7025                scope,
7026                stack,
7027                ancestry,
7028            );
7029            self.parsed
7030                .record_materialization(MaterializationRecord::RecoveredDeclaration {
7031                    recovery: cpp_declaration_range(node),
7032                    unit: class_unit,
7033                });
7034            if uses_initializer_body {
7035                return;
7036            }
7037        }
7038
7039        // A function-like macro invocation that expands to declarations keeps
7040        // no terminator of its own: the semicolons live in the replacement
7041        // body, which is not part of this tree. Tree-sitter recovers one and
7042        // reads the macro name as the member type and its argument list as a
7043        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
7044        // published a field named `ClosedDetect` beside the real constructor
7045        // and split every reference to the constructor's name between the two
7046        // (#3298). The invented terminator is the whole difference from a real
7047        // member: a member terminates itself, and with a terminator the
7048        // grammar reads `T (name);` as a declaration rather than a field.
7049        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
7050            return;
7051        }
7052
7053        let mut handled_function = false;
7054        let mut handled_declarator = false;
7055        let mut cursor = node.walk();
7056        for child in node.named_children(&mut cursor) {
7057            if matches!(
7058                child.kind(),
7059                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7060            ) {
7061                // A named class-like definition remains a declaration even when
7062                // the same statement also declares an object, for example
7063                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
7064                // declaration's type and `kind` as its declarator.  Dropping the
7065                // type here loses both its nested owner and every later lexical
7066                // reference to it.  A body is the structured proof that this is
7067                // a definition rather than an elaborated type use such as
7068                // `class Kind value;`.
7069                if cpp_body_node(child).is_some() {
7070                    self.visit_class_like(child, scope, stack, ancestry);
7071                }
7072                continue;
7073            }
7074        }
7075
7076        let mut cursor = node.walk();
7077        for child in node.children_by_field_name("declarator", &mut cursor) {
7078            if crate::structural::is_recovered_designator_init_declarator(child) {
7079                handled_declarator = true;
7080                continue;
7081            }
7082            if in_class_body
7083                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
7084            {
7085                handled_declarator = true;
7086                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
7087                continue;
7088            }
7089            if let Some(kind) = classify_declarator(child) {
7090                handled_declarator = true;
7091                match kind {
7092                    DeclaratorKind::Function(function_declarator) => {
7093                        handled_function = true;
7094                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
7095                    }
7096                    DeclaratorKind::Variable(variable_declarator) => {
7097                        self.visit_variable_declaration(
7098                            node,
7099                            variable_declarator,
7100                            scope,
7101                            in_class_body,
7102                            ancestry,
7103                        );
7104                    }
7105                }
7106            }
7107        }
7108
7109        if !handled_declarator {
7110            let mut cursor = node.walk();
7111            for child in node.named_children(&mut cursor) {
7112                if crate::structural::is_recovered_designator_init_declarator(child) {
7113                    handled_declarator = true;
7114                    continue;
7115                }
7116                if !is_unfielded_declarator_candidate(child) {
7117                    continue;
7118                }
7119                let Some(kind) = classify_declarator(child) else {
7120                    continue;
7121                };
7122                handled_declarator = true;
7123                match kind {
7124                    DeclaratorKind::Function(function_declarator) => {
7125                        handled_function = true;
7126                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
7127                    }
7128                    DeclaratorKind::Variable(variable_declarator) => {
7129                        self.visit_variable_declaration(
7130                            node,
7131                            variable_declarator,
7132                            scope,
7133                            in_class_body,
7134                            ancestry,
7135                        );
7136                    }
7137                }
7138            }
7139        }
7140
7141        if handled_function {
7142            return;
7143        }
7144
7145        if !handled_declarator {
7146            if in_class_body {
7147                self.visit_class_members_from_declaration(node, scope, ancestry);
7148            } else {
7149                self.visit_global_variables_from_declaration(node, scope, ancestry);
7150            }
7151        }
7152    }
7153
7154    /// Preserve the member structure of an anonymous aggregate.
7155    ///
7156    /// An anonymous union with no declarator promotes its fields into the
7157    /// containing aggregate. An anonymous struct/union followed by a named
7158    /// declarator, such as `struct { T *ops; } sock`, declares both the field
7159    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
7160    /// the declarator's structured nested identity so a later `value.sock.ops`
7161    /// chain can traverse it without parsing a type spelling (#2407).
7162    ///
7163    /// An anonymous aggregate declares no tag, so there is nothing for the C
7164    /// dialect to re-scope and nothing for the C++ dialect to nest: both
7165    /// readings of this shape are the same, and both need these members
7166    /// (#3301).
7167    fn visit_anonymous_aggregate_declaration<'tree>(
7168        &mut self,
7169        node: Node<'tree>,
7170        scope: &ScopeInfo,
7171        in_class_body: bool,
7172        stack: &mut Vec<CppWork<'tree>>,
7173        ancestry: &ParentIndex<'tree>,
7174    ) -> bool {
7175        if !in_class_body || scope.class_unit.is_none() {
7176            return false;
7177        }
7178        let Some(aggregate) = node.child_by_field_name("type") else {
7179            return false;
7180        };
7181        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7182            || aggregate.child_by_field_name("name").is_some()
7183        {
7184            return false;
7185        }
7186        let Some(body) = cpp_body_node(aggregate) else {
7187            return false;
7188        };
7189
7190        let mut cursor = node.walk();
7191        let declarators = node
7192            .children_by_field_name("declarator", &mut cursor)
7193            .filter_map(|declarator| match classify_declarator(declarator) {
7194                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7195                Some(DeclaratorKind::Function(_)) | None => None,
7196            })
7197            .collect::<Vec<_>>();
7198        if declarators.is_empty() {
7199            stack.push(CppWork::Container(CppContainer {
7200                node: body,
7201                scope: scope.clone(),
7202            }));
7203            return true;
7204        }
7205
7206        for declarator in declarators {
7207            let Some(name) = extract_variable_name(declarator, self.source) else {
7208                continue;
7209            };
7210            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
7211            // The receiver type's identity is the declarator's name, so its
7212            // range is the field declaration that carries that name token.
7213            // The inner specifier ends at the closing brace, which leaves the
7214            // name the unit is known by outside its own text (#3287).
7215            self.visit_named_class_like_shape(
7216                aggregate,
7217                name,
7218                Some(body),
7219                true,
7220                Some(cpp_declaration_range(node)),
7221                None,
7222                scope,
7223                stack,
7224                ancestry,
7225            );
7226        }
7227        true
7228    }
7229
7230    /// Give a function-local anonymous aggregate a structured owner so its
7231    /// direct pointer bindings can be typed by the usage graph.  Unlike an
7232    /// anonymous aggregate in a class body, there is no source-level tag or
7233    /// typedef to supply an identity.  The aggregate's CST range is therefore
7234    /// part of a generated, collision-resistant identity; no source spelling
7235    /// is consulted.  The declaration's own variable remains a file-level
7236    /// field projection, while the aggregate body is visited as the generated
7237    /// class's field list.
7238    fn visit_anonymous_local_aggregate_declaration<'tree>(
7239        &mut self,
7240        node: Node<'tree>,
7241        scope: &ScopeInfo,
7242        stack: &mut Vec<CppWork<'tree>>,
7243        ancestry: &ParentIndex<'tree>,
7244    ) -> bool {
7245        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
7246            return false;
7247        }
7248        let Some(aggregate) = node.child_by_field_name("type") else {
7249            return false;
7250        };
7251        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
7252            || aggregate.child_by_field_name("name").is_some()
7253        {
7254            return false;
7255        }
7256        let Some(body) = cpp_body_node(aggregate) else {
7257            return false;
7258        };
7259        let mut cursor = node.walk();
7260        let declarators = node
7261            .children_by_field_name("declarator", &mut cursor)
7262            .filter_map(|declarator| match classify_declarator(declarator) {
7263                Some(DeclaratorKind::Variable(variable)) => Some(variable),
7264                Some(DeclaratorKind::Function(_)) | None => None,
7265            })
7266            .collect::<Vec<_>>();
7267        if declarators.is_empty() {
7268            return false;
7269        }
7270
7271        for declarator in &declarators {
7272            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
7273        }
7274        let name = format!("<anonymous:{}>", aggregate.start_byte());
7275        self.visit_named_class_like_shape(
7276            aggregate,
7277            name,
7278            Some(body),
7279            true,
7280            None,
7281            None,
7282            scope,
7283            stack,
7284            ancestry,
7285        );
7286        true
7287    }
7288
7289    fn visit_function_declaration<'tree>(
7290        &mut self,
7291        declaration_node: Node<'tree>,
7292        declarator: Node<'tree>,
7293        scope: &ScopeInfo,
7294        ancestry: &ParentIndex<'tree>,
7295    ) {
7296        let Some(function) = extract_function_info(declarator, self.source, scope) else {
7297            return;
7298        };
7299        // An in-class member prototype is a real source declaration, not a
7300        // generated or recovery-only unit, so it keeps the ordinary code-unit
7301        // identity (#3508). #3493 recorded its callable modifiers, which is
7302        // what a canonical procedure key needs; a synthetic unit would still
7303        // refuse that key on identity grounds alone.
7304        let code_unit = function.code_unit(self.file.clone());
7305        if self.parsed.contains_declaration(&code_unit) {
7306            self.parsed
7307                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7308            return;
7309        }
7310        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7311        let signature = render_cpp_function_display_signature_from_node(
7312            declaration_node,
7313            self.source,
7314            scope.template_signature.as_deref(),
7315            false,
7316            ancestry,
7317        );
7318        self.parsed.add_signature_with_metadata(
7319            code_unit.clone(),
7320            cpp_signature_metadata(
7321                signature,
7322                declarator,
7323                cpp_callable_linkage(declaration_node, self.source, ancestry),
7324                self.source,
7325                ancestry,
7326            )
7327            .with_declaration_only(true),
7328        );
7329        if let Some(parent) = &scope.class_unit {
7330            self.parsed.add_child(parent.clone(), code_unit);
7331        } else if let Some(module) = &scope.module {
7332            self.parsed.add_child(module.clone(), code_unit);
7333        }
7334    }
7335
7336    fn visit_recovered_macro_qualified_function_declaration<'tree>(
7337        &mut self,
7338        declaration_node: Node<'tree>,
7339        call: Node<'tree>,
7340        scope: &ScopeInfo,
7341        ancestry: &ParentIndex<'tree>,
7342    ) {
7343        let Some(parent) = &scope.class_unit else {
7344            return;
7345        };
7346        let Some(name_node) = call.child_by_field_name("function") else {
7347            return;
7348        };
7349        let Some(arguments) = call.child_by_field_name("arguments") else {
7350            return;
7351        };
7352        let Some((signature, parameter_labels)) =
7353            recovered_macro_qualified_function_parameters(arguments, self.source)
7354        else {
7355            return;
7356        };
7357        let arity = parameter_labels.len();
7358        let function = FunctionInfo {
7359            package_name: scope.package_name.clone(),
7360            owner: Some(CppMemberOwner::Unit(parent.clone())),
7361            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
7362            signature,
7363        };
7364        if function.name.is_empty() {
7365            return;
7366        }
7367        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7368        if self.parsed.contains_declaration(&code_unit) {
7369            self.parsed
7370                .record_navigation_range(code_unit, cpp_declaration_range(declaration_node));
7371            return;
7372        }
7373        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7374        let signature_label = render_cpp_function_display_signature_from_node(
7375            declaration_node,
7376            self.source,
7377            scope.template_signature.as_deref(),
7378            false,
7379            ancestry,
7380        );
7381        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
7382        // Only the callable's parameters came from the reparse: the
7383        // `field_declaration` this recovery reads is a real node in a real
7384        // class body, so its storage class and the body's access ladder state
7385        // this member's modifiers exactly as an ordinary member's do.
7386        let modifiers =
7387            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
7388        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7389            .with_declaration_only(true)
7390            .with_callable_arity(CallableArity::exact(arity))
7391            .with_callable_linkage(linkage)
7392            .with_callable_modifiers(
7393                modifiers.is_static,
7394                modifiers.is_constructor,
7395                modifiers.visibility,
7396            );
7397        self.parsed
7398            .add_signature_with_metadata(code_unit.clone(), metadata);
7399        self.parsed.add_child(parent.clone(), code_unit);
7400    }
7401
7402    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
7403        &mut self,
7404        declaration_node: Node<'tree>,
7405        call: Node<'tree>,
7406        scope: &ScopeInfo,
7407        ancestry: &ParentIndex<'tree>,
7408    ) {
7409        let Some(parent) = &scope.class_unit else {
7410            return;
7411        };
7412        let Some(arguments) = call.child_by_field_name("arguments") else {
7413            return;
7414        };
7415        let Some((mut signature, parameter_labels)) =
7416            recovered_macro_qualified_function_parameters(arguments, self.source)
7417        else {
7418            return;
7419        };
7420        if let Some(template_signature) = &scope.template_signature {
7421            signature = format!("{template_signature}{signature}");
7422        }
7423        let arity = parameter_labels.len();
7424        let function = FunctionInfo {
7425            package_name: scope.package_name.clone(),
7426            owner: Some(CppMemberOwner::Unit(parent.clone())),
7427            name: parent.identifier().to_string(),
7428            signature,
7429        };
7430        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
7431        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7432        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
7433        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
7434        // The recovery already proved this member is the class's constructor:
7435        // the reparsed call names the enclosing class. Everything else is read
7436        // from the real `field_declaration` the recovery found it in.
7437        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
7438        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
7439            .with_declaration_only(false)
7440            .with_callable_arity(CallableArity::exact(arity))
7441            .with_callable_linkage(linkage)
7442            .with_callable_modifiers(
7443                modifiers.is_static,
7444                modifiers.is_constructor,
7445                modifiers.visibility,
7446            );
7447        self.parsed
7448            .add_signature_with_metadata(code_unit.clone(), metadata);
7449        self.parsed.add_child(parent.clone(), code_unit);
7450    }
7451
7452    fn visit_variable_declaration<'tree>(
7453        &mut self,
7454        declaration_node: Node<'tree>,
7455        declarator: Node<'tree>,
7456        scope: &ScopeInfo,
7457        in_class_body: bool,
7458        ancestry: &ParentIndex<'tree>,
7459    ) {
7460        let Some(name) = extract_variable_name(declarator, self.source) else {
7461            return;
7462        };
7463        let parent = if in_class_body {
7464            let Some(parent) = &scope.class_unit else {
7465                return;
7466            };
7467            Some(parent)
7468        } else {
7469            None
7470        };
7471        let short_name = match parent {
7472            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
7473            None => name.clone(),
7474        };
7475        let fq = cpp_leaf_fq(
7476            &scope.package_name,
7477            parent,
7478            &name,
7479            SegmentKind::Member,
7480            SegmentKind::Member,
7481        );
7482        let code_unit = CodeUnit::new_fq(
7483            self.file.clone(),
7484            CodeUnitType::Field,
7485            scope.package_name.clone(),
7486            short_name,
7487            fq,
7488        );
7489        if self.parsed.contains_declaration(&code_unit) {
7490            return;
7491        }
7492        self.add_declaration(code_unit.clone(), declaration_node, None, None);
7493        self.parsed.add_signature_with_metadata(
7494            code_unit.clone(),
7495            SignatureMetadata::new(
7496                render_cpp_field_signature(declaration_node, declarator, self.source),
7497                Vec::new(),
7498            )
7499            .with_cpp_field_linkage(cpp_field_declaration_linkage(
7500                declaration_node,
7501                self.source,
7502                ancestry,
7503            )),
7504        );
7505        if let Some(parent) = &scope.class_unit {
7506            self.parsed.add_child(parent.clone(), code_unit);
7507        } else if let Some(module) = &scope.module {
7508            self.parsed.add_child(module.clone(), code_unit);
7509        }
7510    }
7511
7512    fn visit_class_members_from_declaration<'tree>(
7513        &mut self,
7514        node: Node<'tree>,
7515        scope: &ScopeInfo,
7516        ancestry: &ParentIndex<'tree>,
7517    ) {
7518        let mut cursor = node.walk();
7519        for child in node.named_children(&mut cursor) {
7520            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
7521                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
7522            } else if child.kind() == "init_declarator"
7523                && let Some(inner) = child.child_by_field_name("declarator")
7524            {
7525                self.visit_variable_declaration(node, inner, scope, true, ancestry);
7526            } else if matches!(
7527                child.kind(),
7528                "identifier"
7529                    | "field_identifier"
7530                    | "pointer_declarator"
7531                    | "reference_declarator"
7532                    | "array_declarator"
7533                    | "parenthesized_declarator"
7534            ) {
7535                self.visit_variable_declaration(node, child, scope, true, ancestry);
7536            }
7537        }
7538    }
7539
7540    fn visit_global_variables_from_declaration<'tree>(
7541        &mut self,
7542        node: Node<'tree>,
7543        scope: &ScopeInfo,
7544        ancestry: &ParentIndex<'tree>,
7545    ) {
7546        let mut cursor = node.walk();
7547        for child in node.named_children(&mut cursor) {
7548            if child.kind() == "init_declarator"
7549                && let Some(inner) = child.child_by_field_name("declarator")
7550            {
7551                self.visit_variable_declaration(node, inner, scope, false, ancestry);
7552            } else if matches!(
7553                child.kind(),
7554                "identifier"
7555                    | "field_identifier"
7556                    | "pointer_declarator"
7557                    | "reference_declarator"
7558                    | "array_declarator"
7559                    | "parenthesized_declarator"
7560            ) {
7561                self.visit_variable_declaration(node, child, scope, false, ancestry);
7562            }
7563        }
7564    }
7565
7566    fn visit_type_declaration<'tree>(
7567        &mut self,
7568        node: Node<'tree>,
7569        scope: &ScopeInfo,
7570        stack: &mut Vec<CppWork<'tree>>,
7571        ancestry: &ParentIndex<'tree>,
7572    ) {
7573        let type_node = node.child_by_field_name("type");
7574        if let Some(type_node) = type_node
7575            && matches!(
7576                type_node.kind(),
7577                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
7578            )
7579        {
7580            self.visit_class_like(type_node, scope, stack, ancestry);
7581        }
7582
7583        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
7584            let range = Range {
7585                start_byte: node.start_byte(),
7586                end_byte: recovered.end_node.end_byte(),
7587                start_line: node.start_position().row + 1,
7588                end_line: recovered.end_node.end_position().row + 1,
7589            };
7590            let signature = self
7591                .source
7592                .get(range.start_byte..range.end_byte)
7593                .map(normalize_cpp_whitespace)
7594                .unwrap_or_default();
7595            self.record_type_aliases(
7596                node,
7597                scope,
7598                vec![recovered.name],
7599                signature,
7600                range,
7601                ancestry,
7602            );
7603            return;
7604        }
7605
7606        let alias_names = match node.kind() {
7607            "alias_declaration" => extract_alias_declaration_name(node, self.source)
7608                .into_iter()
7609                .collect::<Vec<_>>(),
7610            "type_definition" => extract_typedef_alias_names(node, self.source),
7611            _ => Vec::new(),
7612        };
7613        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
7614            (type_node, alias_names.as_slice())
7615            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
7616            && type_node.child_by_field_name("name").is_none()
7617        {
7618            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
7619        } else {
7620            None
7621        };
7622        self.add_type_aliases(node, scope, alias_names, ancestry);
7623        if let Some((body, alias_name)) = anonymous_aggregate {
7624            // The typedef alias is also the only user-visible identity of an
7625            // anonymous aggregate. Reuse it as the member owner instead of
7626            // minting a second signatureless class with the same FQN. The
7627            // latter makes forward lookup ambiguous when conditional aliases
7628            // coexist and returns duplicate definitions even without guards.
7629            let signature = normalize_cpp_whitespace(node_text(node, self.source));
7630            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
7631            debug_assert!(self.parsed.contains_declaration(&alias_unit));
7632            let mut nested_scope = scope.clone();
7633            nested_scope.class_unit = Some(alias_unit);
7634            nested_scope.template_signature = scope.template_signature.clone();
7635            nested_scope.template_metadata = None;
7636            nested_scope.declarations_are_fields = false;
7637            nested_scope.recovered_specialization_member_scope = false;
7638            stack.push(CppWork::Container(CppContainer {
7639                node: body,
7640                scope: nested_scope,
7641            }));
7642        }
7643    }
7644
7645    fn add_type_aliases(
7646        &mut self,
7647        node: Node<'_>,
7648        scope: &ScopeInfo,
7649        alias_names: Vec<String>,
7650        ancestry: &ParentIndex<'_>,
7651    ) {
7652        let signature = normalize_cpp_whitespace(node_text(node, self.source));
7653        self.record_type_aliases(
7654            node,
7655            scope,
7656            alias_names,
7657            signature,
7658            cpp_declaration_range(node),
7659            ancestry,
7660        );
7661    }
7662
7663    fn record_type_aliases(
7664        &mut self,
7665        node: Node<'_>,
7666        scope: &ScopeInfo,
7667        alias_names: Vec<String>,
7668        signature: String,
7669        range: Range,
7670        ancestry: &ParentIndex<'_>,
7671    ) {
7672        if signature.is_empty() {
7673            return;
7674        }
7675        let type_name = node
7676            .child_by_field_name("type")
7677            .and_then(|type_node| type_node.child_by_field_name("name"))
7678            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
7679        for alias_name in alias_names {
7680            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
7681                continue;
7682            }
7683            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
7684            // Declaration identity does not include the alias signature. Keep
7685            // each physical range so conditional aliases retain their guards.
7686            self.add_declaration_with_range(code_unit.clone(), range, None, None);
7687            let lexical_scope = cpp_callable_lexical_scope(node, self.source, ancestry);
7688            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
7689                cpp_structured_type_identity(type_node, self.source, &lexical_scope)
7690            });
7691            self.parsed.add_signature_with_metadata(
7692                code_unit.clone(),
7693                SignatureMetadata::new(signature.clone(), Vec::new())
7694                    .with_underlying_type_identity(underlying_type_identity),
7695            );
7696            if let Some(metadata) = &scope.template_metadata {
7697                let mut metadata = metadata.clone();
7698                metadata.primary_fq_name = code_unit.fq_name();
7699                self.parsed
7700                    .set_cpp_template_metadata(code_unit.clone(), metadata);
7701            }
7702            if let Some(parent) = &scope.class_unit {
7703                self.parsed.add_child(parent.clone(), code_unit.clone());
7704            } else if let Some(module) = &scope.module {
7705                self.parsed.add_child(module.clone(), code_unit.clone());
7706            }
7707            self.parsed.mark_type_alias(code_unit);
7708        }
7709    }
7710
7711    fn type_alias_unit(
7712        &self,
7713        scope: &ScopeInfo,
7714        alias_name: String,
7715        signature: String,
7716    ) -> CodeUnit {
7717        let short_name = if let Some(parent) = &scope.class_unit {
7718            cpp_join_nested_short(parent.short_name(), &alias_name)
7719        } else {
7720            alias_name.clone()
7721        };
7722        let fq = cpp_leaf_fq(
7723            &scope.package_name,
7724            scope.class_unit.as_ref(),
7725            &alias_name,
7726            SegmentKind::Nested,
7727            SegmentKind::Type,
7728        );
7729        CodeUnit::with_signature_and_fq(
7730            self.file.clone(),
7731            CodeUnitType::Class,
7732            scope.package_name.clone(),
7733            short_name,
7734            Some(signature),
7735            false,
7736            fq,
7737        )
7738    }
7739
7740    fn visit_macro(&mut self, node: Node<'_>) {
7741        // A comment can terminate tree-sitter's preproc_arg before the logical
7742        // directive ends. Its remaining declarations are replacement locals,
7743        // not file-scope fields, even when recovery exposes them as siblings.
7744        if let Some(replacement) =
7745            crate::graph::syntax::function_macro_replacement_span(node, self.source)
7746        {
7747            self.consumed_fragment_regions
7748                .push((replacement.start, replacement.end));
7749        }
7750        let Some(name) = extract_macro_name(node, self.source) else {
7751            return;
7752        };
7753        let signature = node_text(node, self.source).trim_end().to_string();
7754        if signature.is_empty() {
7755            return;
7756        }
7757        let fq = cpp_member_fq("", &name);
7758        // A macro can be undefined and redefined later in the same file. Its
7759        // structured directive is part of the declaration identity so the
7760        // temporal environment can navigate to the definition active at a
7761        // reference instead of collapsing every spelling to the first range.
7762        // The same physical directive parsed through another C/C++ reading
7763        // still produces the same unit and remains deduplicated.
7764        let code_unit = CodeUnit::with_signature_and_fq(
7765            self.file.clone(),
7766            CodeUnitType::Macro,
7767            "",
7768            name.clone(),
7769            Some(signature.clone()),
7770            false,
7771            fq,
7772        );
7773        if !self.parsed.contains_declaration(&code_unit) {
7774            self.add_declaration(code_unit.clone(), node, None, None);
7775            let name_range = node
7776                .child_by_field_name("name")
7777                .map(cpp_declaration_range)
7778                .unwrap_or_else(|| cpp_declaration_range(node));
7779            self.parsed
7780                .record_materialization(MaterializationRecord::GeneratedDeclaration {
7781                    site: cpp_declaration_range(node),
7782                    argument: name_range,
7783                    kind: GenerationKind::PreprocessorDefinition,
7784                    unit: code_unit.clone(),
7785                });
7786            self.parsed.add_signature(code_unit, signature);
7787        }
7788        if node.kind() == "preproc_def" {
7789            update_object_macro_field_environment(
7790                node,
7791                self.source,
7792                &mut self.object_macro_fields,
7793                &mut self.ambiguous_object_macro_fields,
7794            );
7795        } else {
7796            self.object_macro_fields.remove(&name);
7797            self.ambiguous_object_macro_fields.remove(&name);
7798        }
7799    }
7800
7801    fn visit_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7802        let Some(directive) = node.child_by_field_name("directive") else {
7803            return;
7804        };
7805        let name = node_text(directive, self.source).trim();
7806        let range = cpp_declaration_range(node);
7807        let fields = object_macro_field_closure(&self.object_macro_fields, name);
7808        self.materialize_object_macro_fields(fields, range, scope);
7809    }
7810
7811    /// Bare object-like field-list macros inside an otherwise well-formed
7812    /// aggregate remain identifier nodes in a field declaration. More than
7813    /// one adjacent invocation can be folded into the same declaration, so
7814    /// inspect all of its structured identifier nodes.
7815    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
7816        if !matches!(node.kind(), "declaration" | "field_declaration") {
7817            return false;
7818        }
7819        // Claiming the declaration means its members are accounted for, which
7820        // is only true when there is an owner to materialize them into. A file
7821        // scope reaches this with a declaration that merely *contains* an
7822        // aggregate spelling an invocation (tree-sitter wraps libuv's
7823        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
7824        // `field_declaration` under a malformed header), and consuming it there
7825        // would discard the aggregate itself (#2985).
7826        if scope.class_unit.is_none() {
7827            return false;
7828        }
7829        let macro_nodes =
7830            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
7831        for macro_node in &macro_nodes {
7832            let name = node_text(*macro_node, self.source).trim();
7833            let fields = object_macro_field_closure(&self.object_macro_fields, name);
7834            self.materialize_object_macro_fields(fields, cpp_declaration_range(*macro_node), scope);
7835        }
7836        let Some(last) = macro_nodes.last() else {
7837            return false;
7838        };
7839        // Claiming the declaration accounts for every member it spells, not
7840        // only the donated ones: an invocation folded together with the
7841        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
7842        // leaves those members shapeless, and the grammar recovers them from
7843        // the byte range that follows the invocation.
7844        self.record_collapsed_aggregate_fields(
7845            last.end_byte()..node.end_byte(),
7846            node.start_position().row
7847                + 1
7848                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
7849            scope,
7850        );
7851        true
7852    }
7853
7854    fn materialize_object_macro_fields(
7855        &mut self,
7856        fields: Vec<MacroReplacementField>,
7857        range: Range,
7858        scope: &ScopeInfo,
7859    ) {
7860        let Some(owner) = scope.class_unit.as_ref() else {
7861            return;
7862        };
7863        for field in fields {
7864            let signature = field.declaration.clone();
7865            let mut fq = owner.fq().clone();
7866            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
7867            let short_name = if owner.short_name().is_empty() {
7868                field.name.clone()
7869            } else {
7870                format!("{}.{}", owner.short_name(), field.name)
7871            };
7872            let code_unit = CodeUnit::with_signature_and_fq(
7873                self.file.clone(),
7874                CodeUnitType::Field,
7875                owner.package_name().to_string(),
7876                short_name,
7877                Some(field.declaration),
7878                true,
7879                fq,
7880            );
7881            if self.parsed.contains_declaration(&code_unit) {
7882                continue;
7883            }
7884            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
7885            self.parsed.add_signature(code_unit, signature);
7886        }
7887    }
7888
7889    /// Recover ordinary aggregates whose field-list macro invocations made
7890    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
7891    /// The class markers, aggregate braces, field nodes, and macro identifier
7892    /// nodes are all read from the AST; no source delimiter or token scan is
7893    /// used. A close-brace node inside a malformed field closes the innermost
7894    /// recovered aggregate, preserving nested-owner identity.
7895    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
7896        let mut cursor = node.walk();
7897        let children = node.children(&mut cursor).collect::<Vec<_>>();
7898        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember>)>::new();
7899        let mut object_macro_fields = self.object_macro_fields.clone();
7900        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
7901        let mut open = Vec::<usize>::new();
7902        let mut index = 0;
7903        while index < children.len() {
7904            let keyword = children[index];
7905            if update_object_macro_field_environment(
7906                keyword,
7907                self.source,
7908                &mut object_macro_fields,
7909                &mut ambiguous_object_macro_fields,
7910            ) {
7911                index += 1;
7912                continue;
7913            }
7914            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
7915                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
7916                if !name.is_empty() {
7917                    let parent = open
7918                        .last()
7919                        .and_then(|class| recovered.get(*class))
7920                        .map(|(owner, _, _, _)| owner.clone())
7921                        .or_else(|| scope.class_unit.clone());
7922                    let short_name = parent.as_ref().map_or_else(
7923                        || name.clone(),
7924                        |parent| cpp_join_nested_short(parent.short_name(), &name),
7925                    );
7926                    let fq = cpp_leaf_fq(
7927                        &scope.package_name,
7928                        parent.as_ref(),
7929                        &name,
7930                        SegmentKind::Nested,
7931                        SegmentKind::Type,
7932                    );
7933                    let owner = CodeUnit::with_signature_and_fq(
7934                        self.file.clone(),
7935                        CodeUnitType::Class,
7936                        scope.package_name.clone(),
7937                        short_name,
7938                        None,
7939                        false,
7940                        fq,
7941                    );
7942                    recovered.push((
7943                        owner,
7944                        head.key.start_byte(),
7945                        head.opening.end_byte(),
7946                        Vec::new(),
7947                    ));
7948                    let class_index = recovered.len() - 1;
7949                    match head.folded_members {
7950                        // The ordinary shape leaves the aggregate open: every
7951                        // child that follows belongs to it until a close brace
7952                        // ends it.
7953                        None => open.push(class_index),
7954                        // A folded head carries its own member list and its own
7955                        // closing brace, so it closes here. The invocations that
7956                        // precede the class key are the dangling tail of the
7957                        // replacement that folded it, and belong to whatever
7958                        // aggregate is open around it, not to this one.
7959                        Some(members) => {
7960                            let macro_nodes = object_macro_identifier_nodes_with_environment(
7961                                children[index],
7962                                self.source,
7963                                &mut object_macro_fields,
7964                                &mut ambiguous_object_macro_fields,
7965                            );
7966                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
7967                                .iter()
7968                                .partition(|node| node.start_byte() < head.key.start_byte());
7969                            if let Some(&enclosing) = open.last() {
7970                                for macro_node in preceding {
7971                                    recovered[enclosing]
7972                                        .3
7973                                        .push(CppCollapsedMember::MacroFields {
7974                                            range: cpp_declaration_range(macro_node),
7975                                            fields: object_macro_field_closure(
7976                                                &object_macro_fields,
7977                                                &normalize_cpp_whitespace(node_text(
7978                                                    macro_node,
7979                                                    self.source,
7980                                                )),
7981                                            ),
7982                                        });
7983                                }
7984                            }
7985                            let closing = cpp_collapsed_aggregate_closing_brace(members);
7986                            recovered[class_index]
7987                                .3
7988                                .extend(cpp_collapsed_aggregate_members(
7989                                    &inner,
7990                                    head.opening.end_byte()..closing,
7991                                    head.opening.end_position().row + 1,
7992                                    self.source,
7993                                    &object_macro_fields,
7994                                ));
7995                            recovered[class_index].2 = members.end_byte();
7996                            for &open_class in &open {
7997                                recovered[open_class].2 =
7998                                    recovered[open_class].2.max(members.end_byte());
7999                            }
8000                        }
8001                    }
8002                    index += head.width;
8003                    continue;
8004                }
8005            }
8006            if let Some(&class_index) = open.last()
8007                && children[index].kind() == "field_declaration"
8008            {
8009                let field = children[index];
8010                let macro_nodes = object_macro_identifier_nodes_with_environment(
8011                    field,
8012                    self.source,
8013                    &mut object_macro_fields,
8014                    &mut ambiguous_object_macro_fields,
8015                );
8016                recovered[class_index]
8017                    .3
8018                    .extend(cpp_collapsed_aggregate_members(
8019                        &macro_nodes,
8020                        field.start_byte()..field.end_byte(),
8021                        field.start_position().row + 1,
8022                        self.source,
8023                        &object_macro_fields,
8024                    ));
8025                let end = field.end_byte();
8026                for &open_class in &open {
8027                    recovered[open_class].2 = recovered[open_class].2.max(end);
8028                }
8029                let closes = count_close_brace_nodes(field);
8030                for _ in 0..closes {
8031                    if let Some(closed) = open.pop() {
8032                        recovered[closed].2 = end;
8033                    }
8034                }
8035            }
8036            index += 1;
8037        }
8038
8039        let mut owners = Vec::with_capacity(recovered.len());
8040        for (owner, start, end, members) in recovered {
8041            let parent = owners
8042                .iter()
8043                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
8044                .cloned()
8045                .or_else(|| scope.class_unit.clone());
8046            self.declare_collapsed_aggregate(owner.clone(), start..end, parent, members, scope);
8047            owners.push(owner);
8048        }
8049    }
8050
8051    /// Record one recovered aggregate and the members it declares.
8052    fn declare_collapsed_aggregate(
8053        &mut self,
8054        owner: CodeUnit,
8055        span: std::ops::Range<usize>,
8056        parent: Option<CodeUnit>,
8057        members: Vec<CppCollapsedMember>,
8058        scope: &ScopeInfo,
8059    ) {
8060        let range = Range {
8061            start_byte: span.start,
8062            end_byte: span.end,
8063            start_line: self.source.get(..span.start).map_or(1, |source| {
8064                source.bytes().filter(|byte| *byte == b'\n').count() + 1
8065            }),
8066            end_line: self.source.get(..span.end).map_or(1, |source| {
8067                source.bytes().filter(|byte| *byte == b'\n').count() + 1
8068            }),
8069        };
8070        // The tag can already be declared by a typedef or a forward
8071        // declaration, and this range is the definition's. Record it either
8072        // way: a declaration carries every range it is written at, and
8073        // dropping this one leaves nothing able to prove that the aggregate
8074        // has a body (#3098). Recording a range that is already held, or a
8075        // unit that is already declared, changes nothing.
8076        self.add_declaration_with_range(owner.clone(), range, parent, None);
8077        let owner_scope = ScopeInfo {
8078            class_unit: Some(owner),
8079            declarations_are_fields: true,
8080            ..scope.clone()
8081        };
8082        for member in members {
8083            match member {
8084                CppCollapsedMember::MacroFields { range, fields } => {
8085                    self.materialize_object_macro_fields(fields, range, &owner_scope);
8086                }
8087                CppCollapsedMember::Declarations { span, start_line } => {
8088                    self.record_collapsed_aggregate_fields(span, start_line, &owner_scope);
8089                }
8090            }
8091        }
8092    }
8093
8094    /// Recover an aggregate whose head tree-sitter folded into the declaration
8095    /// that precedes it, outside any collapsed `ERROR` container.
8096    ///
8097    /// A comment inside a field-list macro's replacement ends the replacement
8098    /// token, so the rest of the replacement is read at file scope. When the
8099    /// aggregate that follows declares its member list with one invocation,
8100    /// the parser reads the dangling tail as that declaration's type, the
8101    /// class key as an `ERROR`, and the member list as an initializer (#3098).
8102    /// The invocations before the class key belong to the replacement, not to
8103    /// this aggregate, so they go to whatever aggregate encloses it.
8104    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
8105        if node.parent().is_some_and(|parent| parent.is_error()) {
8106            // The container's own recovery reads this child in source order,
8107            // with the preprocessor environment its earlier children build.
8108            return false;
8109        }
8110        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
8111            return false;
8112        };
8113        let members = head
8114            .folded_members
8115            .expect("a folded aggregate head carries its member list");
8116        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
8117        if name.is_empty() {
8118            return false;
8119        }
8120        let macro_nodes =
8121            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
8122        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
8123            .iter()
8124            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
8125        for macro_node in preceding {
8126            let fields = object_macro_field_closure(
8127                &self.object_macro_fields,
8128                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
8129            );
8130            self.materialize_object_macro_fields(fields, cpp_declaration_range(macro_node), scope);
8131        }
8132        let parent = scope.class_unit.clone();
8133        let short_name = parent.as_ref().map_or_else(
8134            || name.clone(),
8135            |parent| cpp_join_nested_short(parent.short_name(), &name),
8136        );
8137        let fq = cpp_leaf_fq(
8138            &scope.package_name,
8139            parent.as_ref(),
8140            &name,
8141            SegmentKind::Nested,
8142            SegmentKind::Type,
8143        );
8144        let owner = CodeUnit::with_signature_and_fq(
8145            self.file.clone(),
8146            CodeUnitType::Class,
8147            scope.package_name.clone(),
8148            short_name,
8149            None,
8150            false,
8151            fq,
8152        );
8153        let recovered = cpp_collapsed_aggregate_members(
8154            &inner,
8155            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
8156            head.opening.end_position().row + 1,
8157            self.source,
8158            &self.object_macro_fields,
8159        );
8160        self.declare_collapsed_aggregate(
8161            owner,
8162            head.key.start_byte()..members.end_byte(),
8163            parent,
8164            recovered,
8165            scope,
8166        );
8167        true
8168    }
8169
8170    /// Record the members of a collapsed aggregate region as fields of its
8171    /// recovered owner. The names, declaration text, and byte ranges all come
8172    /// from the grammar's reading of the region, so a region that is not a
8173    /// member list contributes nothing.
8174    fn record_collapsed_aggregate_fields(
8175        &mut self,
8176        span: std::ops::Range<usize>,
8177        start_line: usize,
8178        scope: &ScopeInfo,
8179    ) {
8180        let Some(owner) = scope.class_unit.as_ref() else {
8181            return;
8182        };
8183        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
8184            let range = Range {
8185                start_byte: field.range.start,
8186                end_byte: field.range.end,
8187                start_line: start_line
8188                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
8189                end_line: start_line
8190                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
8191            };
8192            let mut fq = owner.fq().clone();
8193            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
8194            let short_name = if owner.short_name().is_empty() {
8195                field.name.clone()
8196            } else {
8197                format!("{}.{}", owner.short_name(), field.name)
8198            };
8199            let signature = normalize_cpp_whitespace(&field.declaration);
8200            let code_unit = CodeUnit::with_signature_and_fq(
8201                self.file.clone(),
8202                CodeUnitType::Field,
8203                owner.package_name().to_string(),
8204                short_name,
8205                Some(signature.clone()),
8206                false,
8207                fq,
8208            );
8209            if self.parsed.contains_declaration(&code_unit) {
8210                continue;
8211            }
8212            self.add_declaration_with_range(code_unit.clone(), range, Some(owner.clone()), None);
8213            self.parsed.add_signature(code_unit, signature);
8214        }
8215    }
8216
8217    fn visit_preproc_call(&mut self, node: Node<'_>, scope: &ScopeInfo) {
8218        let Some(_directive) = node.child_by_field_name("directive") else {
8219            return;
8220        };
8221        if is_cpp_undef_directive(node, self.source) {
8222            update_object_macro_field_environment(
8223                node,
8224                self.source,
8225                &mut self.object_macro_fields,
8226                &mut self.ambiguous_object_macro_fields,
8227            );
8228            return;
8229        }
8230        let directly_in_field_list = node
8231            .parent()
8232            .is_some_and(|parent| parent.kind() == "field_declaration_list");
8233        if scope.class_unit.is_some() && (scope.declarations_are_fields || directly_in_field_list) {
8234            self.visit_object_macro_fields(node, scope);
8235        }
8236    }
8237}
8238
8239/// Every member a field-list macro contributes under `environment`, including
8240/// the members of the field-list macros it composes.
8241///
8242/// Composition is resolved at the invocation, not at the definition, because a
8243/// nested name's active replacement is a property of the environment the
8244/// invocation sees. The traversal is iterative and visits each name once, so a
8245/// self-composing or mutually composing pair terminates.
8246fn object_macro_field_closure(
8247    environment: &HashMap<String, ObjectMacroReplacement>,
8248    name: &str,
8249) -> Vec<MacroReplacementField> {
8250    let mut fields = Vec::new();
8251    let mut visited = HashSet::default();
8252    let mut stack = vec![name.to_string()];
8253    while let Some(current) = stack.pop() {
8254        if !visited.insert(current.clone()) {
8255            continue;
8256        }
8257        let Some(replacement) = environment.get(&current) else {
8258            continue;
8259        };
8260        fields.extend(replacement.fields.iter().cloned());
8261        stack.extend(replacement.nested.iter().rev().cloned());
8262    }
8263    fields
8264}
8265
8266/// The structured replacement an object-like `#define` contributes.
8267///
8268/// The directive's whole logical line is the replacement list, which
8269/// [`object_macro_replacement_span`] recovers, because a comment inside the
8270/// replacement ends tree-sitter's `preproc_arg` token early.
8271fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
8272    crate::graph::syntax::object_macro_replacement_span(node, source)
8273        .and_then(|span| source.get(span))
8274        .map(crate::graph::syntax::object_macro_replacement)
8275        .unwrap_or_default()
8276}
8277
8278/// Apply one preprocessor directive to an object-like field-list environment.
8279/// The environment is intentionally separate from the declaration visitor so
8280/// malformed parser regions can replay directives in source order without
8281/// mutating the live walk's state ahead of those directives.
8282fn update_object_macro_field_environment(
8283    node: Node<'_>,
8284    source: &str,
8285    fields: &mut HashMap<String, ObjectMacroReplacement>,
8286    ambiguous: &mut HashSet<String>,
8287) -> bool {
8288    match node.kind() {
8289        "preproc_def" => {
8290            let Some(name) = extract_macro_name(node, source) else {
8291                return false;
8292            };
8293            let replacement = object_macro_replacement_of(node, source);
8294            if replacement.is_empty() || ambiguous.contains(&name) {
8295                fields.remove(&name);
8296                ambiguous.insert(name);
8297            } else if let Some(previous) = fields.get(&name) {
8298                if previous != &replacement {
8299                    fields.remove(&name);
8300                    ambiguous.insert(name);
8301                }
8302            } else {
8303                fields.insert(name, replacement);
8304            }
8305            true
8306        }
8307        "preproc_call" if is_cpp_undef_directive(node, source) => {
8308            if let Some(argument) = node.child_by_field_name("argument") {
8309                let name = node_text(argument, source).trim();
8310                fields.remove(name);
8311                if inside_preprocessor_conditional(node) {
8312                    ambiguous.insert(name.to_string());
8313                } else {
8314                    ambiguous.remove(name);
8315                }
8316            }
8317            true
8318        }
8319        _ => false,
8320    }
8321}
8322
8323fn object_macro_identifier_nodes<'tree>(
8324    node: Node<'tree>,
8325    source: &str,
8326    fields: &HashMap<String, ObjectMacroReplacement>,
8327) -> Vec<Node<'tree>> {
8328    let mut result = Vec::new();
8329    let mut stack = vec![node];
8330    while let Some(current) = stack.pop() {
8331        if matches!(
8332            current.kind(),
8333            "identifier" | "field_identifier" | "type_identifier"
8334        ) && fields.contains_key(node_text(current, source).trim())
8335        {
8336            result.push(current);
8337        }
8338        let mut cursor = current.walk();
8339        let children = current.children(&mut cursor).collect::<Vec<_>>();
8340        stack.extend(children.into_iter().rev());
8341    }
8342    result.sort_by_key(|node| node.start_byte());
8343    result
8344}
8345
8346/// Collect macro identifiers while replaying any nested preprocessor
8347/// directives in source order. Recovery regions can contain a later `#undef`
8348/// in the same parser error envelope; using the live visitor map for the
8349/// entire envelope would incorrectly materialize invocations after it.
8350fn object_macro_identifier_nodes_with_environment<'tree>(
8351    node: Node<'tree>,
8352    source: &str,
8353    fields: &mut HashMap<String, ObjectMacroReplacement>,
8354    ambiguous: &mut HashSet<String>,
8355) -> Vec<Node<'tree>> {
8356    let mut result = Vec::new();
8357    let mut stack = vec![node];
8358    while let Some(current) = stack.pop() {
8359        if update_object_macro_field_environment(current, source, fields, ambiguous) {
8360            continue;
8361        }
8362        if matches!(
8363            current.kind(),
8364            "identifier" | "field_identifier" | "type_identifier"
8365        ) && fields.contains_key(node_text(current, source).trim())
8366        {
8367            result.push(current);
8368        }
8369        let mut cursor = current.walk();
8370        let children = current.children(&mut cursor).collect::<Vec<_>>();
8371        stack.extend(children.into_iter().rev());
8372    }
8373    result.sort_by_key(|node| node.start_byte());
8374    result
8375}
8376
8377/// One member region of an aggregate whose head and body tree-sitter collapsed
8378/// into an `ERROR` container, in source order.
8379enum CppCollapsedMember {
8380    /// The members a field-list macro invocation donates, all sharing the
8381    /// invocation's range.
8382    MacroFields {
8383        range: Range,
8384        fields: Vec<MacroReplacementField>,
8385    },
8386    /// A byte span whose ordinary member declarations lost their shape with
8387    /// the aggregate body, with the 1-based line the span starts on.
8388    Declarations {
8389        span: std::ops::Range<usize>,
8390        start_line: usize,
8391    },
8392}
8393
8394/// One aggregate head recovered from the children of a collapsed `ERROR`
8395/// container.
8396///
8397/// A member list that opens with an object-like field-list macro invocation
8398/// has no grammar rule, so the aggregate never forms a specifier and its head
8399/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
8400/// class key, the name, and the opening brace are three consecutive children.
8401/// When the aggregate follows the dangling tail of a comment-truncated
8402/// replacement, the parser reads that tail as the declaration's type and folds
8403/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
8404/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
8405/// }` that follows becomes one `field_declaration` whose class key is an
8406/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
8407/// `initializer_list` (#3098).
8408struct CppCollapsedAggregateHead<'tree> {
8409    key: Node<'tree>,
8410    name: Node<'tree>,
8411    opening: Node<'tree>,
8412    /// The member list when the head folded it into its own node, which also
8413    /// carries the aggregate's closing brace. `None` leaves the aggregate open
8414    /// for the children that follow it.
8415    folded_members: Option<Node<'tree>>,
8416    /// How many children the head consumes.
8417    width: usize,
8418}
8419
8420/// The aggregate head that starts at `children[index]`, if one does.
8421fn cpp_collapsed_aggregate_head<'tree>(
8422    children: &[Node<'tree>],
8423    index: usize,
8424    source: &str,
8425) -> Option<CppCollapsedAggregateHead<'tree>> {
8426    let key = *children.get(index)?;
8427    if matches!(key.kind(), "struct" | "class" | "union") {
8428        let name = *children.get(index + 1)?;
8429        let opening = *children.get(index + 2)?;
8430        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
8431            return None;
8432        }
8433        return Some(CppCollapsedAggregateHead {
8434            key,
8435            name,
8436            opening,
8437            folded_members: None,
8438            width: 3,
8439        });
8440    }
8441    cpp_folded_aggregate_head(key, source)
8442}
8443
8444/// The aggregate head tree-sitter folded into one declaration node, reading
8445/// the class key as an `ERROR` and the member list as an initializer.
8446fn cpp_folded_aggregate_head<'tree>(
8447    node: Node<'tree>,
8448    source: &str,
8449) -> Option<CppCollapsedAggregateHead<'tree>> {
8450    if !matches!(node.kind(), "declaration" | "field_declaration") {
8451        return None;
8452    }
8453    let mut cursor = node.walk();
8454    let children = node.children(&mut cursor).collect::<Vec<_>>();
8455    let key_index = children.iter().position(|child| {
8456        child.is_error()
8457            && matches!(
8458                node_text(*child, source).trim(),
8459                "struct" | "class" | "union"
8460            )
8461    })?;
8462    let declarator = *children.get(key_index + 1)?;
8463    // At file scope the grammar has an `init_declarator` for the name and its
8464    // initializer; inside an `ERROR` container the two are loose siblings.
8465    let (name, members) = if declarator.kind() == "init_declarator" {
8466        (
8467            declarator.child_by_field_name("declarator")?,
8468            declarator.child_by_field_name("value")?,
8469        )
8470    } else {
8471        (declarator, *children.get(key_index + 2)?)
8472    };
8473    if !matches!(
8474        name.kind(),
8475        "field_identifier" | "type_identifier" | "identifier"
8476    ) {
8477        return None;
8478    }
8479    if members.kind() != "initializer_list" {
8480        return None;
8481    }
8482    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
8483    Some(CppCollapsedAggregateHead {
8484        key: children[key_index],
8485        name,
8486        opening,
8487        folded_members: Some(members),
8488        width: 1,
8489    })
8490}
8491
8492/// Where a folded member list ends: at its own closing brace when it has one,
8493/// so the reparse of the declarations it holds sees a member list and not a
8494/// stray brace.
8495fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
8496    let mut cursor = members.walk();
8497    members
8498        .children(&mut cursor)
8499        .filter(|child| child.kind() == "}" && !child.is_missing())
8500        .last()
8501        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
8502}
8503
8504/// The members one collapsed aggregate region declares, in source order: what
8505/// its field-list macro invocations donate, then the ordinary declarations
8506/// that follow the last invocation and lost their shape with the body.
8507fn cpp_collapsed_aggregate_members(
8508    macro_nodes: &[Node<'_>],
8509    region: std::ops::Range<usize>,
8510    region_start_line: usize,
8511    source: &str,
8512    environment: &HashMap<String, ObjectMacroReplacement>,
8513) -> Vec<CppCollapsedMember> {
8514    let mut members = macro_nodes
8515        .iter()
8516        .map(|macro_node| CppCollapsedMember::MacroFields {
8517            range: cpp_declaration_range(*macro_node),
8518            fields: object_macro_field_closure(
8519                environment,
8520                &normalize_cpp_whitespace(node_text(*macro_node, source)),
8521            ),
8522        })
8523        .collect::<Vec<_>>();
8524    // Whatever follows the last invocation is an ordinary member list that
8525    // lost its declaration shape with the aggregate's body; the grammar
8526    // recovers it from its own byte range.
8527    let declarations_start = macro_nodes
8528        .last()
8529        .map_or(region.start, |macro_node| macro_node.end_byte());
8530    members.push(CppCollapsedMember::Declarations {
8531        span: declarations_start..region.end,
8532        start_line: region_start_line
8533            + cpp_line_breaks_between(source, region.start, declarations_start),
8534    });
8535    members
8536}
8537
8538/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
8539/// line without rescanning the file from its start.
8540fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
8541    source.get(from..to).map_or(0, |slice| {
8542        slice.bytes().filter(|byte| *byte == b'\n').count()
8543    })
8544}
8545
8546/// How many enclosing aggregates a collapsed member declaration closes: the
8547/// close braces it carries that its own braces do not balance. A member that
8548/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
8549/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
8550/// owner early and lost every member after it (#2985).
8551fn count_close_brace_nodes(node: Node<'_>) -> usize {
8552    let mut opened = 0usize;
8553    let mut closed = 0usize;
8554    let mut stack = vec![node];
8555    while let Some(current) = stack.pop() {
8556        if !current.is_missing() {
8557            match current.kind() {
8558                "{" => opened += 1,
8559                "}" => closed += 1,
8560                _ => {}
8561            }
8562        }
8563        let mut cursor = current.walk();
8564        stack.extend(current.children(&mut cursor));
8565    }
8566    closed.saturating_sub(opened)
8567}
8568
8569/// Classify a C++ field while its declaration syntax is already available.
8570///
8571/// The persisted result lets later visibility queries avoid reparsing the
8572/// complete source file only to recover linkage.
8573pub fn cpp_field_declaration_linkage<'tree>(
8574    declaration: Node<'tree>,
8575    source: &str,
8576    ancestry: &ParentIndex<'tree>,
8577) -> CppFieldLinkage {
8578    let mut current = ancestry.parent(declaration);
8579    let mut enclosed_by_class = false;
8580    while let Some(node) = current {
8581        if node.kind() == "namespace_definition"
8582            && node
8583                .child_by_field_name("name")
8584                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8585        {
8586            return CppFieldLinkage::Internal;
8587        }
8588        if matches!(
8589            node.kind(),
8590            "class_specifier" | "struct_specifier" | "union_specifier"
8591        ) && node
8592            .child_by_field_name("name")
8593            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
8594        {
8595            return CppFieldLinkage::Internal;
8596        }
8597        if matches!(
8598            node.kind(),
8599            "class_specifier" | "struct_specifier" | "union_specifier"
8600        ) {
8601            enclosed_by_class = true;
8602        }
8603        if matches!(node.kind(), "function_definition" | "lambda_expression") {
8604            return CppFieldLinkage::Internal;
8605        }
8606        current = ancestry.parent(node);
8607    }
8608    if enclosed_by_class {
8609        return CppFieldLinkage::External;
8610    }
8611    let mut cursor = declaration.walk();
8612    let mut has_static = false;
8613    let mut has_extern = false;
8614    let mut has_inline = false;
8615    let mut has_const = false;
8616    let mut has_constexpr = false;
8617    for child in declaration.named_children(&mut cursor) {
8618        let text = normalize_cpp_whitespace(node_text(child, source));
8619        match (child.kind(), text.as_str()) {
8620            ("storage_class_specifier", "static") => has_static = true,
8621            ("storage_class_specifier", "extern") => has_extern = true,
8622            ("storage_class_specifier", "inline") => has_inline = true,
8623            ("storage_class_specifier", "constexpr") => has_constexpr = true,
8624            ("type_qualifier", "const") => has_const = true,
8625            ("type_qualifier", "constexpr") => has_constexpr = true,
8626            _ => {}
8627        }
8628    }
8629    if has_static {
8630        CppFieldLinkage::Internal
8631    } else if has_extern || has_inline {
8632        CppFieldLinkage::External
8633    } else if has_const || has_constexpr {
8634        CppFieldLinkage::InternalUnlessExternalPeer
8635    } else {
8636        CppFieldLinkage::External
8637    }
8638}
8639
8640fn cpp_declaration_range(node: Node<'_>) -> Range {
8641    Range {
8642        start_byte: node.start_byte(),
8643        end_byte: node.end_byte(),
8644        start_line: node.start_position().row + 1,
8645        end_line: node.end_position().row + 1,
8646    }
8647}
8648
8649/// A recovery interval as a [`Range`], for materialization records whose
8650/// window is a byte region rather than one parser node (the sentinel-macro
8651/// region reparses, issue #941/#1657).
8652fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
8653    let line_at = |byte: usize| {
8654        source.as_bytes()[..byte]
8655            .iter()
8656            .filter(|&&b| b == b'\n')
8657            .count()
8658            + 1
8659    };
8660    Range {
8661        start_byte,
8662        end_byte,
8663        start_line: line_at(start_byte),
8664        end_line: line_at(end_byte),
8665    }
8666}
8667
8668/// Every `#include` directive the tree holds, in source order.
8669///
8670/// A preorder sweep rather than a step of the declaration walk: the container
8671/// walk descends only through declaration scopes, so an include written inside
8672/// a class body or a function body would otherwise never be seen, and an
8673/// include is an include wherever it is written.
8674pub fn collect_cpp_includes(root: Node<'_>, source: &str, parsed: &mut ParsedFile) {
8675    walk_named_tree_preorder(root, true, |node| {
8676        if node.kind() == "preproc_include" {
8677            let raw = normalize_cpp_whitespace(node_text(node, source));
8678            if !raw.is_empty() {
8679                parsed.imports.push(ImportInfo {
8680                    raw_snippet: raw,
8681                    is_wildcard: false,
8682                    is_global: false,
8683                    identifier: None,
8684                    alias: None,
8685                    path: None,
8686                    binder_span: None,
8687                });
8688            }
8689            return WalkControl::SkipChildren;
8690        }
8691        WalkControl::Continue
8692    });
8693}
8694
8695pub fn recover_quoted_includes(source: &str, parsed: &mut ParsedFile) {
8696    let mut in_block_comment = false;
8697    for line in source.lines() {
8698        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
8699        let trimmed = stripped.trim();
8700        if !looks_like_quoted_include_line(trimmed) {
8701            continue;
8702        }
8703
8704        let raw = normalize_cpp_whitespace(trimmed);
8705        // The tree-sitter walk already recorded every `#include` it could see;
8706        // this line scan only recovers the ones a parse error hid, so skip a
8707        // snippet that is already an import binding.
8708        if parsed
8709            .imports
8710            .iter()
8711            .any(|import| import.raw_snippet == raw)
8712        {
8713            continue;
8714        }
8715
8716        parsed.imports.push(ImportInfo {
8717            raw_snippet: raw,
8718            is_wildcard: false,
8719            is_global: false,
8720            identifier: None,
8721            alias: None,
8722            path: None,
8723            binder_span: None,
8724        });
8725    }
8726}
8727
8728fn looks_like_quoted_include_line(line: &str) -> bool {
8729    let Some(rest) = line.trim_start().strip_prefix('#') else {
8730        return false;
8731    };
8732    let Some(rest) = rest.trim_start().strip_prefix("include") else {
8733        return false;
8734    };
8735    rest.trim_start().starts_with('"')
8736}
8737
8738fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
8739    let mut raw = Vec::new();
8740    if let Some(bases) = cpp_class_like_base_clause(node) {
8741        collect_cpp_base_nodes(bases, source, &mut raw);
8742    }
8743    raw
8744}
8745
8746/// The `base_class_clause` a class-like node writes, if it writes one.
8747///
8748/// The pinned grammar exposes the clause as a named child rather than a field,
8749/// so the question is asked by kind. A base clause is the only place a C++
8750/// class body records that some other class may declare a member, which is
8751/// what [`cpp_callable_dispatch_extensibility`] reads it for.
8752fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
8753    let mut cursor = class_like.walk();
8754    class_like
8755        .named_children(&mut cursor)
8756        .find(|child| child.kind() == "base_class_clause")
8757}
8758
8759fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
8760    walk_named_tree_preorder(node, false, |child| match child.kind() {
8761        "type_identifier" | "qualified_identifier" | "template_type" => {
8762            let text = normalize_cpp_whitespace(node_text(child, source));
8763            if !text.is_empty() {
8764                raw.push(text);
8765            }
8766            WalkControl::SkipChildren
8767        }
8768        _ => WalkControl::Continue,
8769    });
8770}
8771
8772fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
8773    let mut out = String::new();
8774    let chars: Vec<char> = line.chars().collect();
8775    let mut index = 0;
8776    let mut in_string = false;
8777    let mut in_char = false;
8778    let mut escape = false;
8779
8780    while index < chars.len() {
8781        let ch = chars[index];
8782        let next = chars.get(index + 1).copied();
8783
8784        if *in_block_comment {
8785            if ch == '*' && next == Some('/') {
8786                *in_block_comment = false;
8787                index += 2;
8788            } else {
8789                index += 1;
8790            }
8791            continue;
8792        }
8793
8794        if in_string {
8795            out.push(ch);
8796            if escape {
8797                escape = false;
8798            } else if ch == '\\' {
8799                escape = true;
8800            } else if ch == '"' {
8801                in_string = false;
8802            }
8803            index += 1;
8804            continue;
8805        }
8806
8807        if in_char {
8808            out.push(ch);
8809            if escape {
8810                escape = false;
8811            } else if ch == '\\' {
8812                escape = true;
8813            } else if ch == '\'' {
8814                in_char = false;
8815            }
8816            index += 1;
8817            continue;
8818        }
8819
8820        if ch == '/' && next == Some('/') {
8821            break;
8822        }
8823        if ch == '/' && next == Some('*') {
8824            *in_block_comment = true;
8825            index += 2;
8826            continue;
8827        }
8828        if ch == '"' {
8829            in_string = true;
8830            out.push(ch);
8831            index += 1;
8832            continue;
8833        }
8834        if ch == '\'' {
8835            in_char = true;
8836            out.push(ch);
8837            index += 1;
8838            continue;
8839        }
8840
8841        out.push(ch);
8842        index += 1;
8843    }
8844
8845    out
8846}
8847
8848#[derive(Clone)]
8849struct FunctionInfo {
8850    package_name: String,
8851    owner: Option<CppMemberOwner>,
8852    name: String,
8853    signature: String,
8854}
8855
8856/// Owner of a member function, kept structured so a literal `$` inside a
8857/// source-level class name never crosses a join/split boundary: the legacy
8858/// `$`-joined owner string was re-split at fq construction, dropping a leading
8859/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
8860/// and tripping the package/short boundary assert -- the #2140 corruption one
8861/// level up (#2362).
8862#[derive(Clone)]
8863enum CppMemberOwner {
8864    /// Source-level owner class chain from a qualified declarator-id, one
8865    /// class name per component (`Outer::Inner::method` -> `["Outer",
8866    /// "Inner"]`); each component may itself contain a literal `$`.
8867    Chain(Vec<String>),
8868    /// The lexically enclosing or recovered class unit; the member fq extends
8869    /// its fq directly instead of re-splitting its `$`-joined short chain.
8870    Unit(CodeUnit),
8871}
8872
8873impl CppMemberOwner {
8874    /// The legacy `$`-joined owner chain used in the member's short name.
8875    fn short_chain(&self) -> String {
8876        match self {
8877            Self::Chain(chain) => chain.join("$"),
8878            Self::Unit(parent) => parent.short_name().to_string(),
8879        }
8880    }
8881}
8882
8883enum DeclaratorKind<'a> {
8884    Function(Node<'a>),
8885    Variable(Node<'a>),
8886}
8887
8888impl FunctionInfo {
8889    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
8890        self.code_unit_with_synthetic(file, false)
8891    }
8892
8893    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
8894        let short_name = match &self.owner {
8895            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
8896            None => self.name.clone(),
8897        };
8898        let fq = match &self.owner {
8899            Some(CppMemberOwner::Chain(chain)) => {
8900                debug_assert!(
8901                    !chain.is_empty(),
8902                    "an empty owner chain is no owner; producers return None instead"
8903                );
8904                let mut fq = FqName::new();
8905                cpp_push_package(&mut fq, &self.package_name);
8906                let mut first = true;
8907                for component in chain {
8908                    let kind = if first {
8909                        SegmentKind::Type
8910                    } else {
8911                        SegmentKind::Nested
8912                    };
8913                    fq.push(cpp_segment(component, kind));
8914                    first = false;
8915                }
8916                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8917                fq
8918            }
8919            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
8920                .fq()
8921                .clone()
8922                .with_pushed(cpp_segment(&self.name, SegmentKind::Member)),
8923            // An anonymous parent (empty short chain) contributes no owner
8924            // segment -- the same guard as cpp_join_member_short above.
8925            Some(CppMemberOwner::Unit(_)) | None => {
8926                let mut fq = FqName::new();
8927                cpp_push_package(&mut fq, &self.package_name);
8928                fq.push(cpp_segment(&self.name, SegmentKind::Member));
8929                fq
8930            }
8931        };
8932        CodeUnit::with_signature_and_fq(
8933            file,
8934            CodeUnitType::Function,
8935            self.package_name.clone(),
8936            short_name,
8937            Some(self.signature.clone()),
8938            synthetic,
8939            fq,
8940        )
8941    }
8942}
8943
8944fn extract_function_info(
8945    declarator: Node<'_>,
8946    source: &str,
8947    scope: &ScopeInfo,
8948) -> Option<FunctionInfo> {
8949    let parameters_node = declarator.child_by_field_name("parameters")?;
8950    let declarator_name_node = declarator
8951        .child_by_field_name("declarator")
8952        .or_else(|| parameters_node.prev_named_sibling())?;
8953    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
8954}
8955
8956fn extract_function_info_from_name(
8957    declarator: Node<'_>,
8958    declarator_name_node: Node<'_>,
8959    source: &str,
8960    scope: &ScopeInfo,
8961) -> Option<FunctionInfo> {
8962    let parameters_node = declarator.child_by_field_name("parameters")?;
8963    let parameters_text = cpp_parameter_signature(parameters_node, source);
8964    let recovered_specialization_member = scope
8965        .recovered_specialization_member_scope
8966        .then(|| {
8967            let terminal = declarator_name_node
8968                .child_by_field_name("name")
8969                .unwrap_or(declarator_name_node);
8970            let name = canonical_cpp_qualified_component(terminal, source)?.name;
8971            let owner = scope.class_unit.as_ref()?;
8972            Some((
8973                Some(CppMemberOwner::Unit(owner.clone())),
8974                name,
8975                scope.package_name.clone(),
8976            ))
8977        })
8978        .flatten();
8979    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
8980        parts
8981    } else if let Some(parts) =
8982        split_structured_templated_cpp_name(declarator_name_node, source, scope)
8983    {
8984        parts
8985    } else {
8986        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
8987            declarator_name_node,
8988            source,
8989        )?);
8990        if raw_name.is_empty() {
8991            return None;
8992        }
8993        split_cpp_name(&raw_name, scope)
8994    };
8995    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
8996    let mut signature = if suffix.is_empty() {
8997        parameters_text
8998    } else {
8999        format!("{parameters_text} {suffix}")
9000    };
9001    if let Some(template_signature) = &scope.template_signature {
9002        signature = format!("{template_signature}{signature}");
9003    }
9004
9005    Some(FunctionInfo {
9006        package_name,
9007        owner,
9008        name,
9009        signature,
9010    })
9011}
9012
9013/// Recover the semantic return type and callable name when a declaration macro
9014/// occupies a function definition's `type` field. Tree-sitter either exposes a
9015/// scalar return as the declarator's apparent name and the callable as the sole
9016/// identifier in an `ERROR`, or joins a template return and callable into a
9017/// qualified identifier with a missing `::`. Both shapes retain the complete
9018/// parameter list and body; a concrete separator remains an out-of-line member.
9019fn cpp_macro_displaced_callable_parts<'tree>(
9020    function_declarator: Node<'tree>,
9021    source: &str,
9022    ancestry: &ParentIndex<'tree>,
9023) -> Option<(Node<'tree>, Node<'tree>)> {
9024    let definition = ancestry.parent(function_declarator)?;
9025    if definition.kind() != "function_definition"
9026        || definition.child_by_field_name("declarator") != Some(function_declarator)
9027        || definition
9028            .child_by_field_name("body")
9029            .is_none_or(|body| body.kind() != "compound_statement")
9030    {
9031        return None;
9032    }
9033    let macro_type = definition.child_by_field_name("type")?;
9034    if macro_type.kind() != "type_identifier"
9035        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
9036    {
9037        return None;
9038    }
9039
9040    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
9041    if apparent_return_type.kind() == "qualified_identifier"
9042        && let (Some(return_type), Some(callable_name)) = (
9043            apparent_return_type.child_by_field_name("scope"),
9044            apparent_return_type.child_by_field_name("name"),
9045        )
9046        && return_type.kind() == "template_type"
9047        && matches!(callable_name.kind(), "identifier" | "field_identifier")
9048        && (0..apparent_return_type.child_count())
9049            .filter_map(|index| apparent_return_type.child(index))
9050            .any(|child| child.kind() == "::" && child.is_missing())
9051        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
9052        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
9053    {
9054        return Some((return_type, callable_name));
9055    }
9056    if !matches!(
9057        apparent_return_type.kind(),
9058        "identifier" | "field_identifier" | "type_identifier"
9059    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
9060    {
9061        return None;
9062    }
9063    let parameters = function_declarator.child_by_field_name("parameters")?;
9064    let mut cursor = function_declarator.walk();
9065    let between = function_declarator
9066        .named_children(&mut cursor)
9067        .filter(|child| child.kind() != "comment")
9068        .filter(|child| {
9069            child.start_byte() >= apparent_return_type.end_byte()
9070                && child.end_byte() <= parameters.start_byte()
9071                && !same_node(*child, apparent_return_type)
9072                && !same_node(*child, parameters)
9073        })
9074        .collect::<Vec<_>>();
9075    let [name_error] = between.as_slice() else {
9076        return None;
9077    };
9078    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
9079        return None;
9080    }
9081    let callable_name = name_error.named_child(0)?;
9082    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
9083        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
9084    {
9085        return None;
9086    }
9087    Some((apparent_return_type, callable_name))
9088}
9089
9090/// The part of a `function_declarator` after its parameter list that belongs to
9091/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
9092/// specification, a trailing return type and a trailing requires-clause.
9093///
9094/// The grammar makes each of these a distinct sibling of the `parameters`
9095/// field, so they are read from the tree. Splitting the declarator's text on
9096/// the parameter list instead silently dropped every qualifier whenever the
9097/// parameter list was spelled with whitespace that normalization rewrote - a
9098/// line break or a double space was enough to make a `const` member definition
9099/// a different logical symbol from its declaration (#1827).
9100///
9101/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
9102/// are deliberately excluded. C++ does not make them part of the signature and
9103/// an out-of-line definition never repeats them, so including them would split
9104/// a declaration from its own definition.
9105fn cpp_declarator_identity_suffix(
9106    declarator: Node<'_>,
9107    parameters_node: Node<'_>,
9108    source: &str,
9109) -> String {
9110    let mut cursor = declarator.walk();
9111    let parts = declarator
9112        .named_children(&mut cursor)
9113        .filter(|child| child.start_byte() >= parameters_node.end_byte())
9114        .filter(|child| {
9115            matches!(
9116                child.kind(),
9117                "type_qualifier"
9118                    | "ref_qualifier"
9119                    | "noexcept"
9120                    | "throw_specifier"
9121                    | "trailing_return_type"
9122                    | "requires_clause"
9123            )
9124        })
9125        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
9126        .filter(|text| !text.is_empty())
9127        .collect::<Vec<_>>();
9128    normalize_cpp_qualifier_suffix(&parts.join(" "))
9129}
9130
9131/// The identity suffix of one callable declarator, for a consumer that holds
9132/// the declarator rather than the declaration walk's parts.
9133///
9134/// The persisted signature concatenates the parameter spelling and this suffix,
9135/// so a comparison that must agree on the suffix alone recomputes it here
9136/// instead of splitting the stored string.
9137pub(crate) fn cpp_callable_identity_suffix(
9138    function_declarator: Node<'_>,
9139    source: &str,
9140) -> Option<String> {
9141    let parameters_node = function_declarator.child_by_field_name("parameters")?;
9142    Some(cpp_declarator_identity_suffix(
9143        function_declarator,
9144        parameters_node,
9145        source,
9146    ))
9147}
9148
9149pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
9150    match classify_declarator(node)? {
9151        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
9152        DeclaratorKind::Variable(_) => None,
9153    }
9154}
9155
9156fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
9157    match node.kind() {
9158        "function_declarator" => {
9159            let inner = node
9160                .child_by_field_name("declarator")
9161                .or_else(|| node.child_by_field_name("name"))
9162                .or_else(|| last_named_child(node));
9163            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
9164                Some(DeclaratorKind::Variable(node))
9165            } else {
9166                Some(DeclaratorKind::Function(node))
9167            }
9168        }
9169        "init_declarator"
9170        | "pointer_declarator"
9171        | "reference_declarator"
9172        | "parenthesized_declarator"
9173        | "array_declarator"
9174        | "attributed_declarator"
9175        | "template_function" => node
9176            .child_by_field_name("declarator")
9177            .or_else(|| node.child_by_field_name("name"))
9178            .or_else(|| last_named_child(node))
9179            .and_then(classify_declarator),
9180        "identifier" | "field_identifier" | "qualified_identifier" => {
9181            Some(DeclaratorKind::Variable(node))
9182        }
9183        _ => node
9184            .child_by_field_name("declarator")
9185            .or_else(|| node.child_by_field_name("name"))
9186            .or_else(|| last_named_child(node))
9187            .and_then(classify_declarator),
9188    }
9189}
9190
9191fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
9192    matches!(
9193        node.kind(),
9194        "function_declarator"
9195            | "init_declarator"
9196            | "pointer_declarator"
9197            | "reference_declarator"
9198            | "parenthesized_declarator"
9199            | "array_declarator"
9200            | "attributed_declarator"
9201            | "template_function"
9202            | "identifier"
9203            | "field_identifier"
9204            | "qualified_identifier"
9205    )
9206}
9207
9208fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
9209    let class_like = first_class_like_child(node);
9210    let mut cursor = node.walk();
9211    node.named_children(&mut cursor).any(|child| {
9212        matches!(
9213            child.kind(),
9214            "init_declarator"
9215                | "pointer_declarator"
9216                | "reference_declarator"
9217                | "array_declarator"
9218                | "function_declarator"
9219                | "parenthesized_declarator"
9220                | "attributed_declarator"
9221        ) || matches!(
9222            child.kind(),
9223            "identifier" | "field_identifier" | "qualified_identifier"
9224        ) && class_like.is_none_or(|class_node| {
9225            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
9226        })
9227    })
9228}
9229
9230/// One namespace-scope forward class declaration that a recovered export-macro
9231/// class definition may borrow its identity from.  Tree-sitter can close a
9232/// malformed class at the enclosing namespace's closing brace, leaving the later
9233/// class definitions as root-level recovered `function_definition` nodes.  A
9234/// preceding `class Name;` in the same namespace is the only structured identity
9235/// signal available in that shape.
9236///
9237/// Everything recorded here is a property of the forward declaration alone.
9238/// What depends on the node doing the asking -- that the forward and its
9239/// namespace both close before it, with nothing but recovery trivia between --
9240/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
9241/// tree answers every later question about it.
9242struct CppNamespaceForward {
9243    name: String,
9244    start_byte: usize,
9245    /// Where the malformed namespace that held the forward ended.  No query
9246    /// asks anything else about that node.
9247    namespace_end_byte: usize,
9248    package_name: String,
9249}
9250
9251/// Read `node` as a borrowable namespace forward declaration.
9252///
9253/// The admission is deliberately conservative: only a body-less class specifier
9254/// whose declaration has no declarator, at namespace scope rather than inside a
9255/// function or class body, in a namespace that itself failed to parse.
9256fn cpp_namespace_forward_entry<'tree>(
9257    node: Node<'tree>,
9258    source: &str,
9259    ancestry: &ParentIndex<'tree>,
9260) -> Option<CppNamespaceForward> {
9261    if !matches!(
9262        node.kind(),
9263        "class_specifier" | "struct_specifier" | "union_specifier"
9264    ) || cpp_body_node(node).is_some()
9265    {
9266        return None;
9267    }
9268    let parent = node.parent()?;
9269    if !(parent.kind() == "declaration_list"
9270        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
9271    {
9272        return None;
9273    }
9274    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
9275    // Borrowing is only justified by the parser-recovery shape we are
9276    // repairing: the namespace that held the forward must itself contain a
9277    // syntax error. A clean, unrelated namespace forward is not an identity
9278    // proof.
9279    if !namespace.has_error() {
9280        return None;
9281    }
9282    Some(CppNamespaceForward {
9283        name: class_like_name(node, source, ancestry)?,
9284        start_byte: node.start_byte(),
9285        namespace_end_byte: namespace.end_byte(),
9286        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
9287    })
9288}
9289
9290/// Whether `forward` stands in the recovery relation to the node asking about
9291/// it: it and its malformed namespace both closed before the recovered class,
9292/// and nothing but recovery trivia separates the two.
9293fn cpp_namespace_forward_matches_recovery(
9294    forward: &CppNamespaceForward,
9295    recovered_node: Node<'_>,
9296) -> bool {
9297    forward.start_byte < recovered_node.start_byte()
9298        && forward.namespace_end_byte < recovered_node.start_byte()
9299        && malformed_namespace_is_nearest_recovery_region(
9300            forward.namespace_end_byte,
9301            recovered_node,
9302        )
9303}
9304
9305/// What one open [`CppVisitor::record_recovered_declarations`] has watched
9306/// happen to the declaration set.
9307///
9308/// The recovered set used to be a difference against a clone of the whole
9309/// declaration set, taken once per recovery: O(recoveries x declarations) on
9310/// exactly the error-recovered files that already walk slowest (#2787). The
9311/// walk knows which declarations it creates, so the capture collects them as
9312/// they are made and the difference is never needed.
9313///
9314/// `removed_pre_existing` is what makes that equal to the difference. A
9315/// deferred replacement removes the replaced declaration's children
9316/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
9317/// re-creates them. Creation alone cannot tell that apart from a first mint, so
9318/// a removal of a declaration this capture did not create records that it was
9319/// already there when the capture opened.
9320#[derive(Debug, Default)]
9321pub struct CppRecoveryCapture {
9322    /// Declarations created while this capture was open, in creation order.
9323    created: Vec<CodeUnit>,
9324    /// Membership for `created`.
9325    created_units: HashSet<CodeUnit>,
9326    /// Declarations that predate this capture and have been removed during it.
9327    removed_pre_existing: HashSet<CodeUnit>,
9328}
9329
9330/// Which owners the parse product already holds field declarations for, folded
9331/// in as the walk records them.
9332///
9333/// [`CppVisitor::has_enum_enumerator_units`] asks that question once per enum
9334/// and used to answer it by scanning every declaration accumulated so far:
9335/// O(enums x declarations) on exactly the generated headers that declare many
9336/// of both (#2786). The answer only grows by declaration, so the walk carries
9337/// it. A field's short name names its owner chain, `Owner.member`, so one field
9338/// answers for every dotted prefix of its own short name; an anonymous enum's
9339/// or union's enumerators carry bare short names instead (#2140), which is what
9340/// an empty owner short name asks about.
9341#[derive(Debug, Default)]
9342pub struct CppFieldOwnerIndex {
9343    /// Package name -> the owner short names its fields name.
9344    owners: HashMap<String, HashSet<String>>,
9345    /// Packages holding at least one field that names no owner.
9346    ownerless_packages: HashSet<String>,
9347}
9348
9349impl CppFieldOwnerIndex {
9350    /// The index of the declarations recorded so far, built when the first
9351    /// question arrives.
9352    fn of<'unit>(
9353        declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9354        file: &ProjectFile,
9355    ) -> Self {
9356        let mut index = Self::default();
9357        for declaration in declarations {
9358            index.record(declaration, file);
9359        }
9360        index
9361    }
9362
9363    fn record(&mut self, code_unit: &CodeUnit, file: &ProjectFile) {
9364        if code_unit.kind() != CodeUnitType::Field || code_unit.source() != file {
9365            return;
9366        }
9367        let short_name = code_unit.short_name();
9368        let package_name = code_unit.package_name();
9369        if !short_name.contains(['.', '$']) && !self.ownerless_packages.contains(package_name) {
9370            self.ownerless_packages.insert(package_name.to_string());
9371        }
9372        if !short_name.contains('.') {
9373            return;
9374        }
9375        if !self.owners.contains_key(package_name) {
9376            self.owners
9377                .insert(package_name.to_string(), HashSet::default());
9378        }
9379        let owners = self
9380            .owners
9381            .get_mut(package_name)
9382            .expect("the package entry was just ensured");
9383        for (offset, _) in short_name.match_indices('.') {
9384            let owner = &short_name[..offset];
9385            if !owners.contains(owner) {
9386                owners.insert(owner.to_string());
9387            }
9388        }
9389    }
9390
9391    /// Whether a field declaration in `package_name` names `owner_short_name`
9392    /// as its owner. An empty owner asks about ownerless fields instead.
9393    fn owns_fields(&self, package_name: &str, owner_short_name: &str) -> bool {
9394        if owner_short_name.is_empty() {
9395            self.ownerless_packages.contains(package_name)
9396        } else {
9397            self.owners
9398                .get(package_name)
9399                .is_some_and(|owners| owners.contains(owner_short_name))
9400        }
9401    }
9402}
9403
9404/// The declaration scan [`CppFieldOwnerIndex`] replaces, kept as the oracle a
9405/// debug build asserts every carried answer against and as the release-mode
9406/// parity tests' reference (#2786).
9407#[cfg(any(debug_assertions, test))]
9408fn cpp_declarations_hold_owned_fields<'unit>(
9409    declarations: impl IntoIterator<Item = &'unit CodeUnit>,
9410    file: &ProjectFile,
9411    package_name: &str,
9412    owner_short_name: &str,
9413) -> bool {
9414    let prefix = format!("{owner_short_name}.");
9415    declarations.into_iter().any(|unit| {
9416        unit.kind() == CodeUnitType::Field
9417            && unit.source() == file
9418            && unit.package_name() == package_name
9419            && if owner_short_name.is_empty() {
9420                // Anonymous enum/union parent: its enumerators carry bare
9421                // short names (#2140), so presence means any ownerless
9422                // field in this file.
9423                !unit.short_name().contains(['.', '$'])
9424            } else {
9425                unit.short_name().starts_with(&prefix)
9426            }
9427    })
9428}
9429
9430/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
9431///
9432/// A region reparse is its own tree and is dropped while the walk that made it
9433/// continues, so a later parse can be allocated at the same address and hand out
9434/// the same node ids.  The root's span and shape pin the identity its address
9435/// alone does not: two roots agreeing on all of this are the same parse of the
9436/// same bytes, and a scan of one is a scan of the other.
9437#[derive(PartialEq, Eq, Hash)]
9438pub struct CppTreeIdentity {
9439    root_id: usize,
9440    start_byte: usize,
9441    end_byte: usize,
9442    kind_id: u16,
9443    child_count: usize,
9444}
9445
9446impl CppTreeIdentity {
9447    fn of(root: Node<'_>) -> Self {
9448        Self {
9449            root_id: root.id(),
9450            start_byte: root.start_byte(),
9451            end_byte: root.end_byte(),
9452            kind_id: root.kind_id(),
9453            child_count: root.child_count(),
9454        }
9455    }
9456}
9457
9458/// The namespace forward declarations one tree's prefix holds, folded in as the
9459/// walk asks about them.
9460///
9461/// `scope_for_recovered_exported_class` asks the same question once per
9462/// recovered class, and the answer depends only on the part of the tree that
9463/// starts before the asking node.  Rescanning that prefix per question is
9464/// quadratic in the file, and a generated header whose parse recovery leaves
9465/// thousands of class-like declarations at file scope pays all of it: 1,904
9466/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
9467/// (#2754).  This carries the scan forward instead.  Each question advances the
9468/// traversal from wherever the last one stopped to the asking node's start byte,
9469/// so a whole walk pays at most one pass over the prefix its furthest question
9470/// reaches, and each question then costs a name lookup.
9471#[derive(Default)]
9472pub struct CppNamespaceForwardScan {
9473    /// Every named node starting before this byte has been folded in.
9474    scanned_through: usize,
9475    forwards: HashMap<String, Vec<CppNamespaceForward>>,
9476}
9477
9478impl CppNamespaceForwardScan {
9479    /// Fold in every named node of `root` that starts at or after the fold
9480    /// watermark and before `cutoff`.
9481    ///
9482    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
9483    /// pass owes are exactly the ones no earlier pass reached, and a question
9484    /// about an earlier byte than one already answered costs nothing.
9485    fn advance_to<'tree>(
9486        &mut self,
9487        root: Node<'tree>,
9488        cutoff: usize,
9489        source: &str,
9490        ancestry: &ParentIndex<'tree>,
9491    ) {
9492        if cutoff <= self.scanned_through {
9493            return;
9494        }
9495        let folded_through = self.scanned_through;
9496        let mut cursor = root.walk();
9497        let mut stack = vec![root];
9498        while let Some(current) = stack.pop() {
9499            if (folded_through..cutoff).contains(&current.start_byte())
9500                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9501            {
9502                self.forwards
9503                    .entry(forward.name.clone())
9504                    .or_default()
9505                    .push(forward);
9506            }
9507            // A subtree ending before the watermark holds only nodes an earlier
9508            // pass already folded, and one starting at or after the cutoff is
9509            // outside the prefix being asked about. Skipping both is what keeps
9510            // the total traversal to one pass.
9511            for child in current.named_children(&mut cursor) {
9512                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
9513                    stack.push(child);
9514                }
9515            }
9516        }
9517        self.scanned_through = cutoff;
9518    }
9519
9520    /// The one namespace `name` was forward declared in before `recovered_node`.
9521    /// More than one matching forward declaration is ambiguous and answers
9522    /// nothing rather than guessing.
9523    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
9524        let mut matching = self
9525            .forwards
9526            .get(name)
9527            .into_iter()
9528            .flatten()
9529            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
9530        let first = matching.next()?;
9531        matching
9532            .next()
9533            .is_none()
9534            .then(|| first.package_name.clone())
9535    }
9536}
9537
9538/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
9539/// debug build checks every answer against (and the one the parity tests drive
9540/// directly).  It walks the whole prefix per question, which is exactly the cost
9541/// #2754 removed from the release path.
9542#[cfg(any(debug_assertions, test))]
9543fn unique_earlier_cpp_namespace_forward<'tree>(
9544    recovered_node: Node<'tree>,
9545    name: &str,
9546    source: &str,
9547    ancestry: &ParentIndex<'tree>,
9548) -> Option<String> {
9549    let mut root = recovered_node;
9550    while let Some(parent) = ancestry.parent(root) {
9551        root = parent;
9552    }
9553
9554    let mut candidates = Vec::new();
9555    let mut stack = vec![root];
9556    while let Some(current) = stack.pop() {
9557        if current.start_byte() < recovered_node.start_byte()
9558            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
9559            && forward.name == name
9560            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
9561        {
9562            candidates.push(forward.package_name);
9563        }
9564
9565        let mut cursor = current.walk();
9566        for child in current.named_children(&mut cursor) {
9567            if child.start_byte() < recovered_node.start_byte() {
9568                stack.push(child);
9569            }
9570        }
9571    }
9572
9573    if candidates.len() == 1 {
9574        candidates.pop()
9575    } else {
9576        None
9577    }
9578}
9579
9580fn malformed_namespace_is_nearest_recovery_region(
9581    namespace_end_byte: usize,
9582    recovered_node: Node<'_>,
9583) -> bool {
9584    let mut root = recovered_node;
9585    while let Some(parent) = root.parent() {
9586        root = parent;
9587    }
9588    let mut cursor = root.walk();
9589    root.named_children(&mut cursor)
9590        .filter(|sibling| {
9591            namespace_end_byte <= sibling.start_byte()
9592                && sibling.end_byte() <= recovered_node.start_byte()
9593        })
9594        .all(is_malformed_namespace_recovery_trivia)
9595}
9596
9597fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
9598    matches!(node.kind(), "ERROR" | "comment")
9599        || node.kind().starts_with("preproc_")
9600        || node.kind() == "expression_statement" && node.named_child_count() == 0
9601}
9602
9603/// Return the namespace path for a forward class only when the declaration is
9604/// at namespace scope.  A declaration nested in a function/class body may share
9605/// the same namespace ancestor but cannot identify a top-level class definition.
9606fn cpp_namespace_name_for_forward<'tree>(
9607    node: Node<'tree>,
9608    source: &str,
9609    ancestry: &ParentIndex<'tree>,
9610) -> Option<String> {
9611    cpp_namespace_definition_for_forward(node, ancestry)?;
9612    cpp_lexical_namespace_name(node, source, ancestry)
9613}
9614
9615fn cpp_namespace_definition_for_forward<'tree>(
9616    node: Node<'tree>,
9617    ancestry: &ParentIndex<'tree>,
9618) -> Option<Node<'tree>> {
9619    let declaration = ancestry.parent(node)?;
9620    let mut ancestor = ancestry.parent(declaration);
9621    while let Some(current) = ancestor {
9622        if matches!(
9623            current.kind(),
9624            "compound_statement"
9625                | "field_declaration_list"
9626                | "class_specifier"
9627                | "struct_specifier"
9628                | "union_specifier"
9629                | "function_definition"
9630                | "lambda_expression"
9631        ) {
9632            return None;
9633        }
9634        if current.kind() == "namespace_definition" {
9635            return Some(current);
9636        }
9637        ancestor = ancestry.parent(current);
9638    }
9639    None
9640}
9641
9642fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
9643    match node.kind() {
9644        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9645        "parenthesized_declarator" => node
9646            .child_by_field_name("declarator")
9647            .or_else(|| last_named_child(node))
9648            .is_some_and(is_pointer_wrapper_declarator),
9649        "template_function" => node
9650            .child_by_field_name("name")
9651            .is_some_and(is_function_pointer_like_inner_declarator),
9652        _ => false,
9653    }
9654}
9655
9656fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
9657    match node.kind() {
9658        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
9659        "parenthesized_declarator" => node
9660            .child_by_field_name("declarator")
9661            .or_else(|| last_named_child(node))
9662            .is_some_and(is_pointer_wrapper_declarator),
9663        _ => false,
9664    }
9665}
9666
9667fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
9668    let cleaned = raw_name.trim_start_matches("template ").trim();
9669    // A leading `::` is the explicit-global marker, not an empty owner segment.
9670    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
9671    // erroneous macro envelope swallowing the first identifier of an
9672    // out-of-line `X::X` constructor, chromium #1573); without this strip the
9673    // split below yields owner_parts `[""]`, constructing a unit with an empty
9674    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
9675    let cleaned = cleaned.trim_start_matches("::");
9676    // Parser recovery can preserve two adjacent scope operators around a
9677    // missing component (for example `X::/**/::method` in compiler diagnostic
9678    // fixtures). Empty components are syntax-recovery artifacts, never C++
9679    // owners. Keeping one as the final owner constructed `short_name=".method"`
9680    // and violated the structured package/short boundary during a large LLVM
9681    // workspace build. This is the same legacy-string-to-FqName bridge as the
9682    // ordinary split above; discard only components that the delimiter itself
9683    // proves empty.
9684    let parts: Vec<_> = cleaned
9685        .split("::")
9686        .filter(|component| !component.is_empty())
9687        .collect();
9688    if parts.is_empty() {
9689        return (None, cleaned.to_string(), scope.package_name.clone());
9690    }
9691    if parts.len() > 1 {
9692        let name = parts.last().unwrap_or(&cleaned).to_string();
9693        let owner_parts = &parts[..parts.len() - 1];
9694        if let Some(class_unit) = &scope.class_unit {
9695            // Lexically inside a class body: the owner is that class, whatever
9696            // the declarator re-qualifies it as.
9697            return (
9698                Some(CppMemberOwner::Unit(class_unit.clone())),
9699                name,
9700                scope.package_name.clone(),
9701            );
9702        }
9703        if !scope.package_name.is_empty() {
9704            // Out-of-line member definition written *inside* an enclosing
9705            // `namespace {}` block (scope package is that namespace). Every
9706            // owner segment before the terminal member is a class-nesting step
9707            // -- an out-of-line nested-class member `Outer::Inner::method` in
9708            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
9709            // namespace path: `using namespace` never brings nested-class
9710            // access into unqualified scope, so C++ always writes the full
9711            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
9712            // that redundantly re-states the enclosing namespace it already
9713            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
9714            // strip that re-qualifying prefix (which duplicates a suffix of the
9715            // enclosing package path) before treating what remains as the
9716            // nested-class chain, so the redundant spelling still lands on the
9717            // same `log4cxx.Foo.method` identity as its header declaration.
9718            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
9719            let owner = (!nested.is_empty()).then(|| {
9720                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
9721            });
9722            return (owner, name, scope.package_name.clone());
9723        }
9724        // File scope (no enclosing `namespace {}` block, scope package empty).
9725        let (owner, package_name) = if owner_parts.len() > 1 {
9726            // A multi-segment qualifier at file scope with no enclosing
9727            // namespace: treat all but the last owner segment as the namespace
9728            // path and the last as the owning class (`ns1::ns2::Class::method`
9729            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
9730            // is really a namespace or an outer class cannot be told from the
9731            // declarator text alone here, and no enclosing namespace or
9732            // in-index owner is available at per-file extraction to confirm the
9733            // class reading, so the far-more-common namespace interpretation is
9734            // kept rather than guessed away (the nested-class-at-file-scope and
9735            // using-directive-qualified nested-class shapes remain on this
9736            // behavior; see #1121).
9737            (
9738                Some(CppMemberOwner::Chain(vec![
9739                    owner_parts.last().unwrap_or(&"").to_string(),
9740                ])),
9741                owner_parts[..owner_parts.len() - 1].join("::"),
9742            )
9743        } else {
9744            // A bare `Class::member` qualifier at file scope carries no
9745            // namespace segment of its own. The declarator alone cannot say
9746            // which namespace owns `Class` -- but a `using namespace X;`
9747            // directive already in effect at this point in the file (#1093,
9748            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
9749            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
9750            // is the remaining structural signal for it, so fall back to it
9751            // rather than leaving the definition's package empty while its
9752            // header declaration (parsed inside the `namespace {}` block) keeps
9753            // the real one -- an identity split that made the same member
9754            // unresolvable under its own displayed spelling.
9755            (
9756                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
9757                cpp_using_directive_namespace_for_bare_owner(scope),
9758            )
9759        };
9760        return (owner, name, package_name);
9761    }
9762
9763    let package_name = scope.package_name.clone();
9764    let owner = scope
9765        .class_unit
9766        .as_ref()
9767        .map(|parent| CppMemberOwner::Unit(parent.clone()));
9768    (owner, cleaned.to_string(), package_name)
9769}
9770
9771/// Drop the leading owner segments of an out-of-line member qualifier that
9772/// merely re-state the enclosing namespace the definition already sits in, so
9773/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
9774/// definition may redundantly write `a::b::Outer::Inner::method` (or the
9775/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
9776/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
9777/// noise, not class-nesting steps. Returns the owner segments with the longest
9778/// such re-qualifying prefix removed (possibly all of them, when the qualifier
9779/// names only the enclosing namespace before the terminal member -- a
9780/// re-qualified free function). `package_name` is the enclosing namespace path
9781/// in its stored `::`-joined form; both sides are split on the same delimiter
9782/// the namespace walker joined them with, so this compares namespace *segments*
9783/// rather than scanning text.
9784fn strip_redundant_namespace_prefix<'a>(
9785    owner_parts: &'a [&'a str],
9786    package_name: &str,
9787) -> &'a [&'a str] {
9788    if package_name.is_empty() {
9789        return owner_parts;
9790    }
9791    let package_segments: Vec<&str> = package_name.split("::").collect();
9792    let max_prefix = owner_parts.len().min(package_segments.len());
9793    for prefix_len in (1..=max_prefix).rev() {
9794        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
9795        if &owner_parts[..prefix_len] == package_suffix {
9796            return &owner_parts[prefix_len..];
9797        }
9798    }
9799    owner_parts
9800}
9801
9802/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
9803/// class name at file/namespace scope, from the `using namespace` directives
9804/// visible at this point in the file. Several may be in scope at once (a
9805/// primary `using namespace NS;` alongside deeper conveniences like `using
9806/// namespace NS::helpers;`); since the declarator gives no way to tell which
9807/// one actually declares the owner class, prefer the shallowest (fewest
9808/// `::`-separated segments) as the file's most likely "home" namespace,
9809/// breaking ties by declaration order. Returns an empty string (leaving the
9810/// caller's package unqualified, as before) when no using-namespace directive
9811/// is in scope.
9812fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
9813    scope
9814        .visible_using_namespaces
9815        .iter()
9816        .min_by_key(|namespace| namespace.split("::").count())
9817        .cloned()
9818        .unwrap_or_default()
9819}
9820
9821struct CppQualifiedNameComponent {
9822    name: String,
9823    is_template_id: bool,
9824}
9825
9826/// Canonical nested-class chain for an out-of-line class definition written
9827/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
9828/// component per class (`["Outer", "Inner"]`).
9829///
9830/// The enclosing namespace fixes the namespace/class boundary: after an
9831/// optional redundant spelling of that namespace, every component belongs to
9832/// the class chain. File-scope qualified class names remain untouched because
9833/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
9834///
9835/// The components stay structured (rather than being `$`-joined here) so the
9836/// fq construction can push one Type/Nested segment per class; the `$`-joined
9837/// short-name display form is derived at the call sites that need it.
9838fn qualified_class_name_chain(
9839    class_node: Node<'_>,
9840    source: &str,
9841    scope: &ScopeInfo,
9842) -> Option<Vec<String>> {
9843    if scope.package_name.is_empty() || scope.class_unit.is_some() {
9844        return None;
9845    }
9846    let name = class_node.child_by_field_name("name")?;
9847    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
9848    if explicitly_global
9849        || components.len() < 2
9850        || components.iter().any(|component| component.is_template_id)
9851    {
9852        return None;
9853    }
9854    let names = components
9855        .iter()
9856        .map(|component| component.name.as_str())
9857        .collect::<Vec<_>>();
9858    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
9859    if class_chain.is_empty() {
9860        return None;
9861    }
9862    Some(class_chain.iter().map(|name| name.to_string()).collect())
9863}
9864
9865fn structured_cpp_qualified_components(
9866    qualified_name: Node<'_>,
9867    source: &str,
9868) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
9869    if qualified_name.kind() != "qualified_identifier" {
9870        return None;
9871    }
9872
9873    let mut components = Vec::new();
9874    let mut current = qualified_name;
9875    let mut explicitly_global = false;
9876    loop {
9877        if current.kind() == "qualified_identifier" {
9878            if let Some(component) = current.child_by_field_name("scope") {
9879                components.push(canonical_cpp_qualified_component(component, source)?);
9880            } else if components.is_empty() {
9881                explicitly_global = true;
9882            } else {
9883                return None;
9884            }
9885            current = current.child_by_field_name("name")?;
9886        } else {
9887            components.push(canonical_cpp_qualified_component(current, source)?);
9888            break;
9889        }
9890    }
9891    Some((components, explicitly_global))
9892}
9893
9894fn split_structured_templated_cpp_name(
9895    declarator_name: Node<'_>,
9896    source: &str,
9897    scope: &ScopeInfo,
9898) -> Option<(Option<CppMemberOwner>, String, String)> {
9899    let (mut components, explicitly_global) =
9900        structured_cpp_qualified_components(declarator_name, source)?;
9901
9902    let terminal = components.pop()?;
9903    let owner_start = components
9904        .iter()
9905        .position(|component| component.is_template_id)?;
9906    let explicit_package = components[..owner_start]
9907        .iter()
9908        .map(|component| component.name.as_str())
9909        .collect::<Vec<_>>()
9910        .join("::");
9911    let explicit_package_is_empty = explicit_package.is_empty();
9912    let package_name = match (
9913        explicitly_global,
9914        scope.package_name.is_empty(),
9915        explicit_package_is_empty,
9916    ) {
9917        (true, _, _) => explicit_package,
9918        (false, _, true) => scope.package_name.clone(),
9919        (false, true, false) => explicit_package,
9920        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
9921    };
9922    // Same identity-split fallback as `split_cpp_name` (#1093): a template
9923    // specialization's owner class named with no namespace segment of its own
9924    // (`explicit_package` empty) at file scope (`explicitly_global` false)
9925    // with nothing enclosing (`package_name` still empty) has no structural
9926    // signal for its namespace besides an in-scope `using namespace X;`.
9927    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
9928    {
9929        cpp_using_directive_namespace_for_bare_owner(scope)
9930    } else {
9931        package_name
9932    };
9933    let owner_chain = components[owner_start..]
9934        .iter()
9935        .map(|component| component.name.clone())
9936        .collect::<Vec<_>>();
9937    if owner_chain.is_empty() || terminal.name.is_empty() {
9938        return None;
9939    }
9940
9941    Some((
9942        Some(CppMemberOwner::Chain(owner_chain)),
9943        terminal.name,
9944        package_name,
9945    ))
9946}
9947
9948fn canonical_cpp_qualified_component(
9949    mut component: Node<'_>,
9950    source: &str,
9951) -> Option<CppQualifiedNameComponent> {
9952    let mut is_template_id = false;
9953    loop {
9954        match component.kind() {
9955            "template_type" => {
9956                is_template_id = true;
9957                component = component.child_by_field_name("name")?;
9958            }
9959            "dependent_name" => component = component.named_child(0)?,
9960            "identifier"
9961            | "field_identifier"
9962            | "namespace_identifier"
9963            | "type_identifier"
9964            | "operator_name"
9965            | "destructor_name" => {
9966                let name = normalize_cpp_whitespace(node_text(component, source));
9967                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
9968                    name,
9969                    is_template_id,
9970                });
9971            }
9972            _ => component = component.child_by_field_name("name")?,
9973        }
9974    }
9975}
9976
9977fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
9978    if let Some(name) = macro_decorated_unqualified_name(node) {
9979        return extract_declarator_name(name, source);
9980    }
9981    match node.kind() {
9982        "identifier"
9983        | "field_identifier"
9984        | "type_identifier"
9985        | "operator_name"
9986        | "destructor_name"
9987        | "qualified_identifier" => node_text(node, source).to_string(),
9988        "function_declarator"
9989        | "pointer_declarator"
9990        | "reference_declarator"
9991        | "parenthesized_declarator"
9992        | "array_declarator"
9993        | "template_function" => node
9994            .child_by_field_name("declarator")
9995            .or_else(|| node.child_by_field_name("name"))
9996            .or_else(|| last_named_child(node))
9997            .map(|child| extract_declarator_name(child, source))
9998            .unwrap_or_else(|| node_text(node, source).to_string()),
9999        _ => node
10000            .child_by_field_name("name")
10001            .map(|child| extract_declarator_name(child, source))
10002            .unwrap_or_else(|| node_text(node, source).to_string()),
10003    }
10004}
10005
10006/// Extract a callable identity only through declaration-shaped AST nodes.
10007/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
10008/// expose the call's parameter list as a false function declarator; accepting
10009/// arbitrary node text there emitted bogus names such as `.*f`.
10010fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10011    if let Some(name) = macro_decorated_unqualified_name(node) {
10012        return extract_callable_declarator_name(name, source);
10013    }
10014    match node.kind() {
10015        "identifier"
10016        | "field_identifier"
10017        | "type_identifier"
10018        | "operator_name"
10019        | "destructor_name"
10020        | "qualified_identifier" => Some(node_text(node, source).to_string()),
10021        "function_declarator"
10022        | "pointer_declarator"
10023        | "reference_declarator"
10024        | "parenthesized_declarator"
10025        | "array_declarator"
10026        | "template_function" => node
10027            .child_by_field_name("declarator")
10028            .or_else(|| node.child_by_field_name("name"))
10029            .and_then(|child| extract_callable_declarator_name(child, source)),
10030        _ => None,
10031    }
10032}
10033
10034fn extract_variable_name(node: Node<'_>, source: &str) -> Option<String> {
10035    match node.kind() {
10036        "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
10037            let name = node_text(node, source).trim().to_string();
10038            (!name.is_empty()).then_some(name)
10039        }
10040        _ => node
10041            .child_by_field_name("declarator")
10042            .or_else(|| node.child_by_field_name("name"))
10043            .or_else(|| last_named_child(node))
10044            .and_then(|child| extract_variable_name(child, source)),
10045    }
10046}
10047
10048/// Recover a C field whose name is wrapped in the function-like
10049/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
10050/// function declarator, while retaining its argument as a structured child.
10051/// The exact macro name and one-argument shape keep ordinary function-pointer
10052/// fields and unknown malformed declarators fail-closed.
10053#[derive(Clone, Copy)]
10054pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
10055    pub(crate) name: Node<'tree>,
10056    pub(crate) declarator: Node<'tree>,
10057}
10058
10059impl RecoveredFunctionLikeFieldDeclarator<'_> {
10060    pub(crate) fn pointer_depth(self) -> i32 {
10061        let mut depth = 0;
10062        let mut current = self.declarator;
10063        while current.kind() != "function_declarator" {
10064            if current.kind() == "pointer_declarator" {
10065                depth += 1;
10066            }
10067            current = current
10068                .child_by_field_name("declarator")
10069                .expect("recovered field wrapper has an inner declarator");
10070        }
10071        depth
10072    }
10073}
10074
10075/// Whether a class-scope field declaration is really a function-like macro
10076/// invocation whose replacement the parser never saw.
10077///
10078/// The invocation carries no terminator of its own, so tree-sitter invents a
10079/// zero-width `;` and reads the macro name as the member's type and the
10080/// argument list as a `parenthesized_declarator`. That shape is not a member:
10081/// a member terminates itself, and with a real terminator the grammar reads
10082/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
10083/// type can name the macro being invoked; a builtin type keeps whatever
10084/// recovery the ordinary declarator walk makes of it.
10085fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
10086    if node.kind() != "field_declaration" {
10087        return false;
10088    }
10089    let Some(type_node) = node.child_by_field_name("type") else {
10090        return false;
10091    };
10092    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
10093        return false;
10094    }
10095    let Some(declarator) = node.child_by_field_name("declarator") else {
10096        return false;
10097    };
10098    if declarator.kind() != "parenthesized_declarator" {
10099        return false;
10100    }
10101    let mut invented_terminator = false;
10102    for child in children_iter(node) {
10103        if child.kind() != ";" {
10104            continue;
10105        }
10106        if !child.is_missing() {
10107            return false;
10108        }
10109        invented_terminator = true;
10110    }
10111    invented_terminator
10112}
10113
10114pub(crate) fn recovered_function_like_field_declarator<'tree>(
10115    node: Node<'tree>,
10116    source: &str,
10117) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
10118    if node.kind() != "field_declaration" {
10119        return None;
10120    }
10121    let outer_declarator = node.child_by_field_name("declarator")?;
10122    let mut declarator = outer_declarator;
10123    while matches!(
10124        declarator.kind(),
10125        "pointer_declarator"
10126            | "reference_declarator"
10127            | "array_declarator"
10128            | "parenthesized_declarator"
10129    ) {
10130        declarator = declarator.child_by_field_name("declarator")?;
10131    }
10132    if declarator.kind() != "function_declarator" {
10133        return None;
10134    }
10135    let macro_name = declarator.child_by_field_name("declarator")?;
10136    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
10137    {
10138        return None;
10139    }
10140    let parameters = declarator.child_by_field_name("parameters")?;
10141    let mut cursor = parameters.walk();
10142    let mut arguments = parameters.named_children(&mut cursor);
10143    let parameter = arguments.next()?;
10144    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
10145        return None;
10146    }
10147    let name = parameter.child_by_field_name("type").filter(|argument| {
10148        matches!(
10149            argument.kind(),
10150            "identifier" | "field_identifier" | "type_identifier"
10151        )
10152    })?;
10153    Some(RecoveredFunctionLikeFieldDeclarator {
10154        name,
10155        declarator: outer_declarator,
10156    })
10157}
10158
10159fn last_named_child(node: Node<'_>) -> Option<Node<'_>> {
10160    let count = node.named_child_count();
10161    if count == 0 {
10162        None
10163    } else {
10164        node.named_child(count - 1)
10165    }
10166}
10167
10168fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
10169    let name_node = node.child_by_field_name("name")?;
10170    let name = normalize_cpp_whitespace(node_text(name_node, source));
10171    (!name.is_empty()).then_some(name)
10172}
10173
10174fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10175    if node.kind() != "declaration" {
10176        return Vec::new();
10177    }
10178    let Some(keyword) = node.child_by_field_name("type").filter(|node| {
10179        node.kind() == "type_identifier" && matches!(node_text(*node, source), "using" | "typedef")
10180    }) else {
10181        return Vec::new();
10182    };
10183    let Some(declarator) = node.child_by_field_name("declarator") else {
10184        return Vec::new();
10185    };
10186    if node_text(keyword, source) == "using"
10187        && (declarator.kind() != "init_declarator"
10188            || declarator.child_by_field_name("value").is_none())
10189    {
10190        return Vec::new();
10191    }
10192    if node_text(keyword, source) == "typedef"
10193        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
10194    {
10195        return vec![alias_name];
10196    }
10197    extract_typedef_declarator_name(declarator, source)
10198        .into_iter()
10199        .collect()
10200}
10201
10202fn recovered_typedef_error_alias_name(
10203    declaration: Node<'_>,
10204    declarator: Node<'_>,
10205    source: &str,
10206) -> Option<String> {
10207    // An export macro between `class` and its name can make tree-sitter parse
10208    // the recovered class body as a function body. In that shape,
10209    //
10210    //     typedef spi::Filter BASE_CLASS;
10211    //
10212    // becomes a declaration whose `declarator` is the underlying qualified
10213    // type (`spi::Filter`) and whose actual alias name is displaced into the
10214    // following ERROR node. Do not publish the terminal underlying type
10215    // (`Filter`) as a false class-owned alias.
10216    if declarator.kind() != "qualified_identifier" {
10217        return None;
10218    }
10219    let mut cursor = declaration.walk();
10220    let mut errors = declaration
10221        .named_children(&mut cursor)
10222        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
10223    let error = errors.next()?;
10224    if errors.next().is_some() || error.named_child_count() != 1 {
10225        return None;
10226    }
10227    let name = error.named_child(0)?;
10228    if !matches!(
10229        name.kind(),
10230        "identifier" | "field_identifier" | "type_identifier"
10231    ) {
10232        return None;
10233    }
10234    let name = normalize_cpp_whitespace(node_text(name, source));
10235    (!name.is_empty()).then_some(name)
10236}
10237
10238fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
10239    // A function-like token in the type position can make tree-sitter expose
10240    // its argument as a parenthesized declarator. Do not publish that argument
10241    // as an alias. The macro-specific recovery below handles the proven shape.
10242    if fragmented_parenthesized_typedef_type(node).is_some() {
10243        return Vec::new();
10244    }
10245    let has_function_like_macro_type = node
10246        .child_by_field_name("type")
10247        .filter(|type_node| type_node.kind() == "type_identifier")
10248        .is_some_and(|type_node| {
10249            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10250        });
10251    let mut names = Vec::new();
10252    let mut cursor = node.walk();
10253    for declarator in node.children_by_field_name("declarator", &mut cursor) {
10254        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
10255            continue;
10256        }
10257        if let Some(name) = extract_typedef_declarator_name(declarator, source)
10258            && !names.contains(&name)
10259        {
10260            names.push(name);
10261        }
10262    }
10263    names
10264}
10265
10266struct RecoveredMacroTypedefAlias<'tree> {
10267    name: String,
10268    end_node: Node<'tree>,
10269}
10270
10271/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
10272/// into an identifier expression statement. The uppercase macro token, missing
10273/// typedef terminator, and complete sibling terminator prove this exact shape.
10274fn recovered_macro_typedef_alias<'tree>(
10275    node: Node<'tree>,
10276    source: &str,
10277) -> Option<RecoveredMacroTypedefAlias<'tree>> {
10278    let type_node = fragmented_parenthesized_typedef_type(node)?;
10279    if type_node.kind() != "type_identifier"
10280        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
10281    {
10282        return None;
10283    }
10284
10285    let end_node = node.next_named_sibling()?;
10286    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
10287        return None;
10288    }
10289    let name_node = end_node.named_child(0)?;
10290    if name_node.kind() != "identifier" {
10291        return None;
10292    }
10293    let has_terminator = (0..end_node.child_count()).any(|index| {
10294        end_node
10295            .child(index)
10296            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
10297    });
10298    if !has_terminator {
10299        return None;
10300    }
10301    let name = normalize_cpp_whitespace(node_text(name_node, source));
10302    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
10303}
10304
10305fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
10306    if node.kind() != "type_definition" {
10307        return None;
10308    }
10309    let mut declarator_cursor = node.walk();
10310    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
10311    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
10312        return None;
10313    }
10314    let has_missing_terminator = (0..node.child_count()).any(|index| {
10315        node.child(index)
10316            .is_some_and(|child| child.kind() == ";" && child.is_missing())
10317    });
10318    if !has_missing_terminator {
10319        return None;
10320    }
10321    node.child_by_field_name("type")
10322}
10323
10324fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
10325    match node.kind() {
10326        "identifier" | "field_identifier" | "type_identifier" => {
10327            let name = normalize_cpp_whitespace(node_text(node, source));
10328            (!name.is_empty()).then_some(name)
10329        }
10330        "qualified_identifier" => node
10331            .child_by_field_name("name")
10332            .and_then(|name| extract_typedef_declarator_name(name, source)),
10333        _ => node
10334            .child_by_field_name("declarator")
10335            .or_else(|| node.child_by_field_name("name"))
10336            .or_else(|| last_named_child(node))
10337            .and_then(|child| extract_typedef_declarator_name(child, source)),
10338    }
10339}
10340
10341fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
10342    let name = node
10343        .child_by_field_name("name")
10344        .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10345        .or_else(|| {
10346            let mut cursor = node.walk();
10347            node.named_children(&mut cursor)
10348                .find(|child| {
10349                    matches!(
10350                        child.kind(),
10351                        "identifier" | "field_identifier" | "type_identifier"
10352                    )
10353                })
10354                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
10355        })?;
10356    (!name.is_empty()).then_some(name)
10357}
10358
10359fn same_node(left: Node<'_>, right: Node<'_>) -> bool {
10360    left.id() == right.id()
10361}
10362
10363fn render_cpp_type_signature(
10364    node: Node<'_>,
10365    source: &str,
10366    template_signature: Option<&str>,
10367) -> String {
10368    let text = normalize_cpp_whitespace(node_text(node, source));
10369    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
10370    let rendered = if head.ends_with(';') {
10371        head.to_string()
10372    } else {
10373        format!("{head} {{")
10374    };
10375    if let Some(template_signature) = template_signature {
10376        format!("template {template_signature} {rendered}")
10377    } else {
10378        rendered
10379    }
10380}
10381
10382fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
10383    if let Some(recovered) = recovered_pyobject_head_field(node, source)
10384        && recovered.declarator == declarator
10385    {
10386        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
10387        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
10388        return format!("{type_text} {declarator};");
10389    }
10390    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
10391        && recovered.name == declarator
10392    {
10393        let type_text = node
10394            .child_by_field_name("type")
10395            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
10396            .unwrap_or_default();
10397        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
10398        let mut prefix = String::new();
10399        let mut suffix = String::new();
10400        let mut current = recovered.declarator;
10401        while current.kind() != "function_declarator" {
10402            match current.kind() {
10403                "pointer_declarator" => prefix.push('*'),
10404                "reference_declarator" => prefix.push('&'),
10405                "array_declarator" => {
10406                    let size = current
10407                        .child_by_field_name("size")
10408                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
10409                        .unwrap_or_default();
10410                    suffix.push('[');
10411                    suffix.push_str(&size);
10412                    suffix.push(']');
10413                }
10414                "parenthesized_declarator" => {}
10415                _ => unreachable!("validated recovered field declarator wrapper"),
10416            }
10417            current = current
10418                .child_by_field_name("declarator")
10419                .expect("recovered field wrapper has an inner declarator");
10420        }
10421        let separator = if prefix.is_empty() { "" } else { " " };
10422        return format!("{type_text} {prefix}{separator}{name}{suffix};");
10423    }
10424    if let Some(signature) =
10425        render_recovered_macro_qualified_field_signature(node, declarator, source)
10426    {
10427        return signature;
10428    }
10429    let declaration_text = normalize_cpp_whitespace(node_text(node, source));
10430    let prefix = cpp_declaration_prefix(node, source);
10431    let name = extract_variable_name(declarator, source).unwrap_or_default();
10432    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
10433    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
10434        || (prefix.ends_with('&') && raw_suffix == "&")
10435    {
10436        String::new()
10437    } else {
10438        raw_suffix
10439    };
10440
10441    let mut rendered = if suffix.is_empty() {
10442        format!("{prefix} {name}")
10443    } else if suffix.starts_with('*') || suffix.starts_with('&') {
10444        format!("{prefix}{suffix} {name}")
10445    } else if suffix.starts_with('[') || suffix.starts_with('(') {
10446        format!("{prefix} {name}{suffix}")
10447    } else {
10448        format!("{prefix} {suffix}{name}")
10449    };
10450    rendered = collapse_cpp_whitespace(&rendered);
10451
10452    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10453        format!("{rendered} = {initializer};")
10454    } else if declaration_text.ends_with(';') {
10455        format!("{rendered};")
10456    } else {
10457        rendered
10458    }
10459}
10460
10461fn render_recovered_macro_qualified_field_signature(
10462    node: Node<'_>,
10463    declarator: Node<'_>,
10464    source: &str,
10465) -> Option<String> {
10466    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
10467    if !recovered
10468        .iter()
10469        .any(|candidate| same_node(*candidate, declarator))
10470    {
10471        return None;
10472    }
10473    let pseudo_declarator = node.child_by_field_name("declarator")?;
10474    let mut cursor = node.walk();
10475    let clause = node
10476        .named_children(&mut cursor)
10477        .find(|child| child.kind() == "bitfield_clause")?;
10478    let mut cursor = clause.walk();
10479    let error = clause
10480        .named_children(&mut cursor)
10481        .find(|child| child.kind() == "ERROR")?;
10482    let qualified_type =
10483        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
10484    let prefix = cpp_declaration_prefix(node, source);
10485    let name = extract_variable_name(declarator, source)?;
10486    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
10487    let mut rendered = if suffix.is_empty() {
10488        format!("{prefix} {qualified_type} {name}")
10489    } else {
10490        format!("{prefix} {qualified_type} {suffix} {name}")
10491    };
10492    rendered = collapse_cpp_whitespace(&rendered);
10493
10494    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
10495        Some(format!(
10496            "{rendered} = {};",
10497            normalize_cpp_whitespace(node_text(initializer, source))
10498        ))
10499    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
10500        Some(format!("{rendered} = {initializer};"))
10501    } else {
10502        Some(format!("{rendered};"))
10503    }
10504}
10505
10506fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
10507    match node.kind() {
10508        "pointer_expression" => {
10509            let operator = node
10510                .child_by_field_name("operator")
10511                .or_else(|| node.child(0))
10512                .map(|operator| node_text(operator, source))
10513                .unwrap_or("*");
10514            let argument = node
10515                .child_by_field_name("argument")
10516                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10517                .unwrap_or_default();
10518            format!("{operator}{argument}")
10519        }
10520        "unary_expression" => {
10521            let operator = node
10522                .child_by_field_name("operator")
10523                .or_else(|| node.child(0))
10524                .map(|operator| node_text(operator, source))
10525                .unwrap_or_default();
10526            let argument = node
10527                .child_by_field_name("argument")
10528                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
10529                .unwrap_or_default();
10530            format!("{operator}{argument}")
10531        }
10532        "identifier" | "field_identifier" => String::new(),
10533        _ => cpp_declarator_suffix_without_name(node, source),
10534    }
10535}
10536
10537fn recovered_macro_qualified_field_initializer<'tree>(
10538    clause: Node<'tree>,
10539    declarator: Node<'tree>,
10540) -> Option<Node<'tree>> {
10541    let mut stack = vec![clause];
10542    while let Some(current) = stack.pop() {
10543        if current.kind() == "assignment_expression"
10544            && current
10545                .child_by_field_name("left")
10546                .is_some_and(|left| same_node(left, declarator))
10547        {
10548            return current.child_by_field_name("right");
10549        }
10550        let mut cursor = current.walk();
10551        stack.extend(current.named_children(&mut cursor));
10552    }
10553    None
10554}
10555
10556fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
10557    let text = node_text(node, source);
10558    let mut cursor = node.walk();
10559    let first_declarator = node.named_children(&mut cursor).find(|child| {
10560        matches!(
10561            child.kind(),
10562            "init_declarator"
10563                | "identifier"
10564                | "field_identifier"
10565                | "pointer_declarator"
10566                | "reference_declarator"
10567                | "array_declarator"
10568                | "function_declarator"
10569        )
10570    });
10571    let prefix = if let Some(first_declarator) = first_declarator {
10572        let end = first_declarator
10573            .start_byte()
10574            .saturating_sub(node.start_byte());
10575        let mut prefix = text.get(..end).unwrap_or(text).to_string();
10576        let declarator_suffix = match first_declarator.kind() {
10577            "init_declarator" => first_declarator
10578                .child_by_field_name("declarator")
10579                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
10580                .unwrap_or_default(),
10581            _ => cpp_declarator_suffix_without_name(first_declarator, source),
10582        };
10583        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
10584            prefix.push_str(&declarator_suffix);
10585        }
10586        return collapse_cpp_whitespace(&prefix)
10587            .trim_end_matches(',')
10588            .trim_end_matches(';')
10589            .trim()
10590            .to_string();
10591    } else {
10592        text
10593    };
10594    collapse_cpp_whitespace(prefix)
10595        .trim_end_matches(',')
10596        .trim_end_matches(';')
10597        .trim()
10598        .to_string()
10599}
10600
10601fn cpp_preserved_initializer(
10602    declaration_node: Node<'_>,
10603    declarator: Node<'_>,
10604    source: &str,
10605) -> Option<String> {
10606    let name = extract_variable_name(declarator, source)?;
10607    let mut cursor = declaration_node.walk();
10608    for child in declaration_node.named_children(&mut cursor) {
10609        if child.kind() != "init_declarator" {
10610            continue;
10611        }
10612        let Some(inner) = child.child_by_field_name("declarator") else {
10613            continue;
10614        };
10615        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
10616            continue;
10617        }
10618        let value = child.child_by_field_name("value")?;
10619        let kind = value.kind();
10620        if matches!(
10621            kind,
10622            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
10623        ) {
10624            return Some(normalize_cpp_whitespace(node_text(value, source)));
10625        }
10626        break;
10627    }
10628    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
10629    let pattern = format!(
10630        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
10631        regex::escape(&name)
10632    );
10633    Regex::new(&pattern)
10634        .ok()
10635        .and_then(|regex| regex.captures(&declaration_text))
10636        .and_then(|captures| captures.get(1))
10637        .map(|value| value.as_str().to_string())
10638}
10639
10640fn render_cpp_function_display_signature_from_node<'tree>(
10641    node: Node<'tree>,
10642    source: &str,
10643    template_signature: Option<&str>,
10644    has_body: bool,
10645    ancestry: &ParentIndex<'tree>,
10646) -> String {
10647    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
10648    let parent_text = node_text(root, source);
10649    let body_local_start = root
10650        .child_by_field_name("body")
10651        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
10652        .unwrap_or(parent_text.len());
10653    let display = parent_text
10654        .get(..body_local_start)
10655        .unwrap_or(parent_text)
10656        .trim()
10657        .trim();
10658    let display = if let Some(template_signature) = template_signature {
10659        if display.starts_with("template ") {
10660            display.to_string()
10661        } else {
10662            format!("template {template_signature} {display}")
10663        }
10664    } else {
10665        display.to_string()
10666    };
10667    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
10668    if has_body {
10669        format!("{display} {{...}}")
10670    } else {
10671        format!("{display};")
10672    }
10673}
10674
10675fn cpp_template_signature(
10676    template_node: Node<'_>,
10677    declaration_child: Node<'_>,
10678    source: &str,
10679) -> Option<String> {
10680    let text = source
10681        .get(template_node.start_byte()..declaration_child.start_byte())
10682        .unwrap_or("");
10683    let text = normalize_cpp_whitespace(text);
10684    let start = text.find('<')?;
10685    let end = text.rfind('>')?;
10686    if end < start {
10687        return None;
10688    }
10689    Some(text[start..=end].to_string())
10690}
10691
10692struct RecoveredFragmentedPartialSpecialization<'tree> {
10693    declaration_node: Node<'tree>,
10694    name: String,
10695    range: Range,
10696    prefix_members: Vec<Node<'tree>>,
10697    member_siblings: Vec<Node<'tree>>,
10698    following_declarations: Vec<Node<'tree>>,
10699}
10700
10701struct RecoveredFragmentedPreprocessorClass<'tree> {
10702    declaration_node: Node<'tree>,
10703    class_node: Node<'tree>,
10704    body: Node<'tree>,
10705    name: String,
10706    range: Range,
10707    tail_members: Vec<Node<'tree>>,
10708    member_siblings: Vec<Node<'tree>>,
10709}
10710
10711/// Recover a class whose preprocessor-fragmented parse closes at an early
10712/// member body and publishes the remaining in-class declarations as siblings
10713/// of the surrounding alternative. Primary classes are admitted only when an
10714/// earlier branch contains the matching bodyless declaration and the class
10715/// node retains the displaced `#endif`. Partial specializations instead carry
10716/// their identity structurally in the `template_type` name and template
10717/// metadata. Retain the original AST nodes and re-own only the siblings through
10718/// the displaced structural `};` terminator.
10719fn recover_fragmented_preprocessor_class<'tree>(
10720    template_node: Node<'tree>,
10721    source: &str,
10722    ancestry: &ParentIndex<'tree>,
10723) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
10724    let alternative = ancestry.parent(template_node)?;
10725    if alternative.kind() != "preproc_else" {
10726        return None;
10727    }
10728    let conditional = alternative.parent()?;
10729    if conditional.kind() != "preproc_if" {
10730        return None;
10731    }
10732    let declaration_node = template_node
10733        .named_children(&mut template_node.walk())
10734        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
10735    let class_node = declaration_node
10736        .named_children(&mut declaration_node.walk())
10737        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
10738    let body = cpp_body_node(class_node)?;
10739    if class_node.end_byte() >= declaration_node.end_byte() {
10740        return None;
10741    }
10742    let name = class_like_name(class_node, source, ancestry)?;
10743    let is_partial_specialization = class_node
10744        .child_by_field_name("name")
10745        .is_some_and(|class_name| class_name.kind() == "template_type");
10746    if is_partial_specialization {
10747        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
10748        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
10749            return None;
10750        }
10751    } else {
10752        if !class_has_displaced_preprocessor_terminator(class_node) {
10753            return None;
10754        }
10755        let matching_other_branch = conditional
10756            .named_children(&mut conditional.walk())
10757            .take_while(|child| !same_node(*child, alternative))
10758            .filter(|child| child.kind() == "template_declaration")
10759            .filter_map(first_class_like_child)
10760            .any(|candidate| {
10761                cpp_body_node(candidate).is_none()
10762                    && class_like_name(candidate, source, ancestry).as_deref()
10763                        == Some(name.as_str())
10764            });
10765        if !matching_other_branch {
10766            return None;
10767        }
10768    }
10769
10770    let mut tail_members = Vec::new();
10771    let mut saw_class = false;
10772    let mut declaration_cursor = declaration_node.walk();
10773    for child in declaration_node.named_children(&mut declaration_cursor) {
10774        if same_node(child, class_node) {
10775            saw_class = true;
10776        } else if saw_class {
10777            tail_members.push(child);
10778        }
10779    }
10780
10781    let mut member_siblings = Vec::new();
10782    let mut saw_template = false;
10783    let mut terminator = None;
10784    for index in 0..alternative.child_count() {
10785        let Some(child) = alternative.child(index) else {
10786            continue;
10787        };
10788        if same_node(child, template_node) {
10789            saw_template = true;
10790            continue;
10791        }
10792        if !saw_template {
10793            continue;
10794        }
10795        if displaced_fragmented_class_terminator(alternative, index) {
10796            terminator = alternative.child(index + 1);
10797            break;
10798        }
10799        if child.is_named() {
10800            member_siblings.push(child);
10801        }
10802    }
10803    let terminator = terminator?;
10804    Some(RecoveredFragmentedPreprocessorClass {
10805        declaration_node,
10806        class_node,
10807        body,
10808        name,
10809        range: Range {
10810            start_byte: class_node.start_byte(),
10811            end_byte: terminator.end_byte(),
10812            start_line: class_node.start_position().row + 1,
10813            end_line: terminator.end_position().row + 1,
10814        },
10815        tail_members,
10816        member_siblings,
10817    })
10818}
10819
10820fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
10821    (0..class_node.child_count()).any(|index| {
10822        class_node.child(index).is_some_and(|child| {
10823            child.kind() == "ERROR"
10824                && (0..child.child_count()).any(|error_index| {
10825                    child
10826                        .child(error_index)
10827                        .is_some_and(|token| token.kind() == "#endif")
10828                })
10829        })
10830    })
10831}
10832
10833/// The real `#endif` that tree-sitter consumed inside an error subtree.
10834///
10835/// A preprocessor directive inside a malformed array bound can cause later
10836/// declarations to remain children of the conditional. The non-missing token
10837/// still gives the exact structured boundary. Ignore nested conditionals and
10838/// select the last unpaired error-owned token. Tree-sitter can pair a later
10839/// outer `#endif` with this conditional, so the direct terminator is not
10840/// necessarily missing.
10841pub fn cpp_displaced_preprocessor_terminator<'tree>(
10842    conditional: Node<'tree>,
10843) -> Option<Node<'tree>> {
10844    if !conditional.has_error() {
10845        return None;
10846    }
10847    let has_concrete_direct_terminator = conditional
10848        .child_count()
10849        .checked_sub(1)
10850        .and_then(|index| conditional.child(index))
10851        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
10852    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
10853        // A structured alternative proves that the direct `#endif` closes
10854        // this family. An error-owned terminator inside either branch belongs
10855        // to a damaged nested conditional, not to this one.
10856        return None;
10857    }
10858    // Pair concrete directive tokens while walking the recovered subtree. An
10859    // inner conditional may be represented only by its `#ifndef`/`#endif`
10860    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
10861    // sufficient: an inner `#endif` can otherwise be mistaken for this
10862    // conditional's displaced terminator.
10863    let mut displaced = None;
10864    let mut conditional_depth = 0usize;
10865    let mut stack = children_iter(conditional)
10866        .map(|child| (child, false))
10867        .collect::<Vec<_>>();
10868    stack.reverse();
10869    while let Some((node, inside_error)) = stack.pop() {
10870        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
10871            continue;
10872        }
10873        if node != conditional
10874            && matches!(
10875                node.kind(),
10876                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
10877            )
10878        {
10879            continue;
10880        }
10881        let inside_error = inside_error || node.kind() == "ERROR";
10882        match node.kind() {
10883            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
10884                conditional_depth += 1;
10885            }
10886            "#endif" if !node.is_missing() => {
10887                if conditional_depth == 0
10888                    && inside_error
10889                    && displaced
10890                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
10891                {
10892                    displaced = Some(node);
10893                }
10894                conditional_depth = conditional_depth.saturating_sub(1);
10895            }
10896            _ => {}
10897        }
10898        let first_pushed = stack.len();
10899        stack.extend(children_iter(node).map(|child| (child, inside_error)));
10900        stack[first_pushed..].reverse();
10901    }
10902    displaced
10903}
10904
10905/// The effective end of a conditional whose real terminator tree-sitter
10906/// displaced into declaration recovery.
10907///
10908/// Most damaged conditionals retain a concrete `#endif` token below an
10909/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
10910/// boundary. A preprocessor family that selects the middle of a declaration
10911/// can lose the directive tokens entirely. In that shape tree-sitter leaves
10912/// the declaration's `typedef` token as the sole child of the immediately
10913/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
10914/// declarator name inside the conditional's first declaration. The declaration
10915/// end is then the smallest structured boundary that contains the whole split
10916/// declaration.
10917#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10918pub struct CppDisplacedPreprocessorBoundary {
10919    pub end_byte: usize,
10920    pub end_line: usize,
10921}
10922
10923pub fn cpp_displaced_preprocessor_boundary(
10924    conditional: Node<'_>,
10925) -> Option<CppDisplacedPreprocessorBoundary> {
10926    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
10927        return Some(CppDisplacedPreprocessorBoundary {
10928            end_byte: terminator.end_byte(),
10929            end_line: terminator.end_position().row + 1,
10930        });
10931    }
10932    if let Some(declaration) = displaced_split_declaration(conditional) {
10933        return Some(CppDisplacedPreprocessorBoundary {
10934            end_byte: declaration.end_byte(),
10935            end_line: declaration.end_position().row + 1,
10936        });
10937    }
10938    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
10939        return Some(CppDisplacedPreprocessorBoundary {
10940            end_byte: terminator.end_byte(),
10941            end_line: terminator.end_position().row + 1,
10942        });
10943    }
10944    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
10945        return Some(CppDisplacedPreprocessorBoundary {
10946            end_byte: terminator.end_byte(),
10947            end_line: terminator.end_position().row + 1,
10948        });
10949    }
10950    None
10951}
10952
10953/// Recover an outer terminator that tree-sitter assigned to a damaged nested
10954/// conditional. This occurs when a split construct such as `extern "C"`
10955/// consumes the nested `#endif` inside an error node: the nested conditional's
10956/// direct terminator is then the outer conditional's real terminator, while
10957/// the outer node ends with a missing token and absorbs later declarations.
10958fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10959    if !conditional.has_error()
10960        || conditional.child_by_field_name("alternative").is_some()
10961        || conditional
10962            .child(conditional.child_count().saturating_sub(1))
10963            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
10964    {
10965        return None;
10966    }
10967    let mut recovered = None;
10968    for nested in named_children_iter(conditional) {
10969        if !matches!(
10970            nested.kind(),
10971            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
10972        ) || nested.child_by_field_name("alternative").is_some()
10973        {
10974            continue;
10975        }
10976        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
10977            continue;
10978        };
10979        if direct.kind() != "#endif" || direct.is_missing() {
10980            continue;
10981        }
10982        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
10983            continue;
10984        };
10985        if displaced.end_byte() >= direct.start_byte() {
10986            continue;
10987        }
10988        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
10989            recovered = Some(direct);
10990        }
10991    }
10992    recovered
10993}
10994
10995fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
10996    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
10997        return None;
10998    }
10999    let mut cursor = conditional.walk();
11000    let declarations = conditional
11001        .named_children(&mut cursor)
11002        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
11003        .collect::<Vec<_>>();
11004    let declaration = *declarations.first()?;
11005    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
11006        return None;
11007    }
11008    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
11009    let mut terminator = None;
11010    let mut stack = (0..declaration.child_count())
11011        .filter_map(|index| declaration.child(index))
11012        .filter(|child| child.start_byte() < declarator_start)
11013        .map(|child| (child, false))
11014        .collect::<Vec<_>>();
11015    while let Some((node, inside_error)) = stack.pop() {
11016        let inside_error = inside_error || node.kind() == "ERROR";
11017        if inside_error && node.kind() == "#endif" && !node.is_missing() {
11018            terminator = Some(node);
11019            continue;
11020        }
11021        for index in 0..node.child_count() {
11022            if let Some(child) = node.child(index)
11023                && child.start_byte() < declarator_start
11024            {
11025                stack.push((child, inside_error));
11026            }
11027        }
11028    }
11029    terminator
11030}
11031
11032fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
11033    if !conditional.has_error()
11034        || conditional.child_by_field_name("alternative").is_some()
11035        || conditional
11036            .prev_named_sibling()
11037            .filter(|sibling| {
11038                sibling.kind() == "ERROR"
11039                    && sibling.child_count() == 1
11040                    && sibling
11041                        .child(0)
11042                        .is_some_and(|child| child.kind() == "typedef")
11043            })
11044            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
11045            .is_none()
11046    {
11047        return None;
11048    }
11049    let mut cursor = conditional.walk();
11050    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
11051    let declaration_index = children
11052        .iter()
11053        .position(|child| child.kind() == "declaration" && child.has_error())?;
11054    let declaration = children[declaration_index];
11055    if !children
11056        .iter()
11057        .skip(declaration_index + 1)
11058        .any(|child| child.end_byte() > declaration.end_byte())
11059    {
11060        return None;
11061    }
11062    let declarator = declaration.child_by_field_name("declarator")?;
11063    let mut error_end = None;
11064    let mut names = Vec::new();
11065    let mut stack = vec![declarator];
11066    while let Some(node) = stack.pop() {
11067        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
11068            error_end =
11069                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
11070            continue;
11071        }
11072        if matches!(node.kind(), "identifier" | "type_identifier") {
11073            names.push(node.start_byte());
11074        }
11075        push_named_children_reversed(node, &mut stack);
11076    }
11077    let error_end = error_end?;
11078    names
11079        .into_iter()
11080        .any(|start| start >= error_end)
11081        .then_some(declaration)
11082}
11083
11084fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
11085    let Some(error) = parent.child(error_index) else {
11086        return false;
11087    };
11088    if error.kind() != "ERROR"
11089        || error.child_count() != 1
11090        || error.child(0).is_none_or(|child| child.kind() != "}")
11091    {
11092        return false;
11093    }
11094    let Some(semicolon) = parent.child(error_index + 1) else {
11095        return false;
11096    };
11097    semicolon.kind() == "expression_statement"
11098        && semicolon.child_count() == 1
11099        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
11100}
11101
11102/// Locate the real end of a class-like declaration when a macro invocation
11103/// without a source semicolon absorbs the class's `};` into its parsed field.
11104/// The grammar then keeps following namespace declarations as later children
11105/// of the same field list. The direct ERROR-plus-semicolon pair proves the
11106/// boundary structurally; no source-text delimiter scan is needed.
11107fn displaced_macro_class_tail(
11108    declaration_node: Node<'_>,
11109    body: Node<'_>,
11110    source: &str,
11111) -> Option<DisplacedMacroClassTail> {
11112    if !matches!(
11113        declaration_node.kind(),
11114        "class_specifier" | "struct_specifier" | "union_specifier"
11115    ) || body.kind() != "field_declaration_list"
11116    {
11117        return None;
11118    }
11119
11120    let child_count = body.named_child_count();
11121    for index in 0..child_count {
11122        let child = body.named_child(index)?;
11123        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
11124            continue;
11125        };
11126        let split_index = index + 1;
11127        if split_index >= child_count {
11128            return None;
11129        }
11130        let mut cursor = body.walk();
11131        if !body
11132            .named_children(&mut cursor)
11133            .skip(split_index)
11134            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
11135        {
11136            return None;
11137        }
11138        return Some(DisplacedMacroClassTail {
11139            split_index,
11140            class_range: Range {
11141                start_byte: declaration_node.start_byte(),
11142                end_byte: terminator.end_byte(),
11143                start_line: declaration_node.start_position().row + 1,
11144                end_line: terminator.end_position().row + 1,
11145            },
11146        });
11147    }
11148    None
11149}
11150
11151fn displaced_macro_field_terminator<'tree>(
11152    field: Node<'tree>,
11153    source: &str,
11154) -> Option<Node<'tree>> {
11155    if field.kind() != "field_declaration" {
11156        return None;
11157    }
11158    let macro_type = field.child_by_field_name("type")?;
11159    if macro_type.kind() != "type_identifier"
11160        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
11161        || field.child_by_field_name("declarator")?.kind() != "parenthesized_declarator"
11162    {
11163        return None;
11164    }
11165    for index in 0..field.child_count() {
11166        let error = field.child(index)?;
11167        if error.kind() != "ERROR"
11168            || error.child_count() != 1
11169            || error.child(0).is_none_or(|child| child.kind() != "}")
11170        {
11171            continue;
11172        }
11173        let semicolon = field.child(index + 1)?;
11174        if semicolon.kind() == ";" {
11175            return Some(semicolon);
11176        }
11177    }
11178    None
11179}
11180
11181fn recover_fragmented_partial_specialization<'tree>(
11182    template_node: Node<'tree>,
11183    declaration_child: Node<'tree>,
11184    source: &str,
11185    ancestry: &ParentIndex<'tree>,
11186) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
11187    if declaration_child.kind() != "function_definition" {
11188        return None;
11189    }
11190    let class_node = declaration_child.child_by_field_name("type")?;
11191    if !matches!(
11192        class_node.kind(),
11193        "class_specifier" | "struct_specifier" | "union_specifier"
11194    ) || !class_node
11195        .child_by_field_name("name")
11196        .and_then(|name| direct_identifier_name(name, source))
11197        .is_some_and(|name| cpp_export_macro_token(&name))
11198    {
11199        return None;
11200    }
11201    let declarator = declaration_child.child_by_field_name("declarator")?;
11202    if declarator.kind() != "template_function" {
11203        return None;
11204    }
11205    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
11206    if metadata.specialization_arguments.is_empty() {
11207        return None;
11208    }
11209    let body = declaration_child.child_by_field_name("body")?;
11210    if body.kind() != "compound_statement" {
11211        return None;
11212    }
11213    let complete_prefix = body.named_child(0).filter(|first| {
11214        first.kind() == "labeled_statement"
11215            && first.has_error()
11216            && first
11217                .named_child(first.named_child_count().saturating_sub(1))
11218                .is_some_and(recovered_declaration_has_class_terminator)
11219    });
11220    let complete_body = complete_prefix.is_some();
11221    let mut prefix_members = Vec::new();
11222    if let Some(prefix) = complete_prefix {
11223        prefix_members.push(prefix);
11224    } else {
11225        let mut body_cursor = body.walk();
11226        for child in body.named_children(&mut body_cursor) {
11227            if !is_structurally_valid_fragmented_class_prefix_member(child) {
11228                break;
11229            }
11230            prefix_members.push(child);
11231        }
11232    }
11233    let containing_declarations = template_node.parent()?;
11234    if !matches!(
11235        containing_declarations.kind(),
11236        "declaration_list" | "compound_statement"
11237    ) {
11238        return None;
11239    }
11240    let mut member_siblings = Vec::new();
11241    let mut following_declarations = Vec::new();
11242    let terminator;
11243    if complete_body {
11244        terminator = complete_prefix?;
11245        let mut cursor = body.walk();
11246        let mut after_prefix = false;
11247        for child in body.named_children(&mut cursor) {
11248            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
11249                after_prefix = true;
11250            } else if after_prefix {
11251                following_declarations.push(child);
11252            }
11253        }
11254    } else {
11255        let mut found_template = false;
11256        let mut cursor = containing_declarations.walk();
11257        let mut class_terminator = None;
11258        for child in containing_declarations.children(&mut cursor) {
11259            if same_node(child, template_node) {
11260                found_template = true;
11261                continue;
11262            }
11263            if found_template && child.kind() == "}" {
11264                class_terminator = Some(child);
11265                break;
11266            }
11267            // A namespace can never be a class member: reaching one before the
11268            // terminator proves the class's true close was swallowed upstream
11269            // and this scan has crossed into the enclosing scope, so the
11270            // recovery cannot be bounded -- continuing re-owns the namespace
11271            // block (and its template specializations) as class members under
11272            // a re-appended package, desyncing the fq boundary (#2306).
11273            if found_template && child.kind() == "namespace_definition" {
11274                return None;
11275            }
11276            if found_template && child.is_named() {
11277                member_siblings.push(child);
11278            }
11279        }
11280        terminator = class_terminator?;
11281    }
11282    let name = format!(
11283        "{}<{}>",
11284        metadata.primary_name,
11285        metadata
11286            .specialization_arguments
11287            .iter()
11288            .map(|argument| argument.text.as_str())
11289            .collect::<Vec<_>>()
11290            .join(", ")
11291    );
11292    Some(RecoveredFragmentedPartialSpecialization {
11293        declaration_node: declaration_child,
11294        name,
11295        range: Range {
11296            start_byte: declaration_child.start_byte(),
11297            end_byte: terminator.end_byte(),
11298            start_line: declaration_child.start_position().row + 1,
11299            end_line: terminator.end_position().row + 1,
11300        },
11301        prefix_members,
11302        member_siblings,
11303        following_declarations,
11304    })
11305}
11306
11307/// Whether `node` is the function-shaped parser wrapper whose body was
11308/// recovered as a fragmented partial-specialization class body.
11309///
11310/// Consumers that walk lexical function scopes must use the same complete
11311/// structural proof as declaration extraction. Otherwise member callables in
11312/// the recovered body are mistaken for function-local declarations.
11313pub fn is_recovered_fragmented_partial_specialization_container(
11314    node: Node<'_>,
11315    source: &str,
11316) -> bool {
11317    let Some(template) = node
11318        .parent()
11319        .filter(|parent| parent.kind() == "template_declaration")
11320    else {
11321        return false;
11322    };
11323    let mut root = template;
11324    while let Some(parent) = root.parent() {
11325        root = parent;
11326    }
11327    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
11328        .is_some()
11329}
11330
11331fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
11332    if declaration.kind() != "declaration" {
11333        return false;
11334    }
11335    // With an export macro between `class` and its name, tree-sitter folds a
11336    // complete class body into a function-shaped declaration. The class's own
11337    // `};` remains structurally identifiable as a direct ERROR child holding
11338    // `}`, immediately followed by the declaration's direct `;` child.
11339    (0..declaration.child_count().saturating_sub(1)).any(|index| {
11340        let Some(error) = declaration.child(index) else {
11341            return false;
11342        };
11343        error.kind() == "ERROR"
11344            && error.child_count() == 1
11345            && error.child(0).is_some_and(|child| child.kind() == "}")
11346            && declaration
11347                .child(index + 1)
11348                .is_some_and(|child| child.kind() == ";")
11349    })
11350}
11351
11352fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
11353    if node.has_error() {
11354        return false;
11355    }
11356    match node.kind() {
11357        "declaration"
11358        | "field_declaration"
11359        | "alias_declaration"
11360        | "type_definition"
11361        | "static_assert_declaration" => true,
11362        "labeled_statement" => node
11363            .named_child(node.named_child_count().saturating_sub(1))
11364            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
11365        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
11366            matches!(
11367                child.kind(),
11368                "declaration"
11369                    | "field_declaration"
11370                    | "alias_declaration"
11371                    | "type_definition"
11372                    | "function_definition"
11373            )
11374        }),
11375        _ => false,
11376    }
11377}
11378
11379fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
11380    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
11381        .then(|| node.child_by_field_name("declarator"))
11382        .flatten()
11383        .and_then(|declarator| extract_variable_name(declarator, source))
11384}
11385
11386fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
11387    while let Some(parent) = node.parent() {
11388        if matches!(parent.kind(), "function_definition" | "lambda_expression") {
11389            return true;
11390        }
11391        node = parent;
11392    }
11393    false
11394}
11395
11396fn cpp_template_metadata<'tree>(
11397    template_node: Node<'tree>,
11398    declaration_child: Node<'tree>,
11399    source: &str,
11400    ancestry: &ParentIndex<'tree>,
11401) -> Option<CppTemplateMetadata> {
11402    let parameters_node = template_node.child_by_field_name("parameters")?;
11403    let name_node = cpp_templated_class_name_node(declaration_child)?;
11404    let primary_node = match name_node.kind() {
11405        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
11406        _ => name_node,
11407    };
11408    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
11409    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
11410        return None;
11411    }
11412
11413    let mut parameter_nodes = Vec::new();
11414    let mut parameter_names = Vec::new();
11415    let mut cursor = parameters_node.walk();
11416    for parameter in parameters_node.named_children(&mut cursor) {
11417        if !matches!(
11418            parameter.kind(),
11419            "type_parameter_declaration"
11420                | "optional_type_parameter_declaration"
11421                | "variadic_type_parameter_declaration"
11422                | "template_template_parameter_declaration"
11423                | "parameter_declaration"
11424                | "optional_parameter_declaration"
11425                | "variadic_parameter_declaration"
11426        ) {
11427            continue;
11428        }
11429        let index = parameter_nodes.len();
11430        // An unnamed parameter still contributes template arity and kind. Use
11431        // an impossible C++ identifier so positional reconciliation can bind
11432        // it without making source expressions refer to a name that was not
11433        // written.
11434        let name = cpp_template_parameter_name(parameter, source)
11435            .unwrap_or_else(|| format!("<anonymous:{index}>"));
11436        parameter_names.push(name);
11437        parameter_nodes.push(parameter);
11438    }
11439    let parameters = parameter_nodes
11440        .into_iter()
11441        .zip(parameter_names.iter().cloned())
11442        .map(|(parameter, name)| CppTemplateParameterMetadata {
11443            name,
11444            kind: cpp_template_parameter_kind(parameter),
11445            variadic: matches!(
11446                parameter.kind(),
11447                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
11448            ),
11449            default: cpp_template_parameter_default_expression(
11450                parameter,
11451                source,
11452                &parameter_names,
11453                ancestry,
11454            ),
11455        })
11456        .collect();
11457    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
11458        Vec::new()
11459    } else {
11460        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
11461            .unwrap_or_default()
11462    };
11463    let alias_target = (declaration_child.kind() == "alias_declaration")
11464        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
11465        .flatten();
11466    Some(CppTemplateMetadata {
11467        primary_name,
11468        primary_fq_name: String::new(),
11469        parameters,
11470        specialization_arguments,
11471        alias_target,
11472    })
11473}
11474
11475fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
11476    match node.kind() {
11477        "class_specifier" | "struct_specifier" | "union_specifier" => {
11478            node.child_by_field_name("name")
11479        }
11480        "function_definition" => {
11481            let declarator = node.child_by_field_name("declarator")?;
11482            if matches!(declarator.kind(), "identifier" | "template_function") {
11483                Some(declarator)
11484            } else {
11485                None
11486            }
11487        }
11488        "alias_declaration" => node.child_by_field_name("name"),
11489        _ => None,
11490    }
11491}
11492
11493fn cpp_template_alias_target<'tree>(
11494    alias: Node<'tree>,
11495    source: &str,
11496    parameter_names: &[String],
11497    ancestry: &ParentIndex<'tree>,
11498) -> Option<CppTemplateAliasTargetMetadata> {
11499    let mut type_node = alias.child_by_field_name("type")?;
11500    while type_node.kind() == "type_descriptor" {
11501        type_node = type_node.child_by_field_name("type")?;
11502    }
11503    let global = type_node.child_by_field_name("scope").is_none()
11504        && type_node.child(0).is_some_and(|child| child.kind() == "::");
11505    let mut components = Vec::new();
11506    cpp_template_target_components(type_node, source, &mut components)?;
11507    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
11508    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
11509        components,
11510        global,
11511        arguments,
11512    })
11513}
11514
11515fn cpp_template_target_components(
11516    node: Node<'_>,
11517    source: &str,
11518    out: &mut Vec<String>,
11519) -> Option<()> {
11520    match node.kind() {
11521        "identifier" | "namespace_identifier" | "type_identifier" => {
11522            out.push(node_text(node, source).to_string());
11523            Some(())
11524        }
11525        "template_type" => {
11526            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11527        }
11528        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
11529            if let Some(scope) = node.child_by_field_name("scope") {
11530                cpp_template_target_components(scope, source, out)?;
11531            }
11532            cpp_template_target_components(node.child_by_field_name("name")?, source, out)
11533        }
11534        _ => None,
11535    }
11536}
11537
11538fn cpp_template_argument_expressions<'tree>(
11539    mut node: Node<'tree>,
11540    source: &str,
11541    parameter_names: &[String],
11542    ancestry: &ParentIndex<'tree>,
11543) -> Option<Vec<CppTemplateExpression>> {
11544    loop {
11545        match node.kind() {
11546            "template_type" | "template_function" => {
11547                let arguments = node.child_by_field_name("arguments")?;
11548                let mut cursor = arguments.walk();
11549                return Some(
11550                    arguments
11551                        .named_children(&mut cursor)
11552                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
11553                        .map(|argument| {
11554                            cpp_template_expression(argument, source, parameter_names, ancestry)
11555                        })
11556                        .collect(),
11557                );
11558            }
11559            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
11560                node = node
11561                    .child_by_field_name("name")
11562                    .or_else(|| node.child_by_field_name("type"))?;
11563            }
11564            _ => return None,
11565        }
11566    }
11567}
11568
11569fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
11570    let candidate = node
11571        .child_by_field_name("name")
11572        .or_else(|| node.child_by_field_name("declarator"))
11573        .or_else(|| {
11574            let mut cursor = node.walk();
11575            node.named_children(&mut cursor).find(|child| {
11576                matches!(
11577                    child.kind(),
11578                    "identifier" | "type_identifier" | "field_identifier"
11579                )
11580            })
11581        })?;
11582    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
11583    (!name.is_empty()).then_some(name)
11584}
11585
11586fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
11587    match node.kind() {
11588        "type_parameter_declaration"
11589        | "optional_type_parameter_declaration"
11590        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
11591        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
11592        _ => CppTemplateParameterKind::Value,
11593    }
11594}
11595
11596fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
11597    node.child_by_field_name("default_type")
11598        .or_else(|| node.child_by_field_name("default_value"))
11599}
11600
11601fn cpp_template_parameter_default_expression<'tree>(
11602    parameter: Node<'tree>,
11603    source: &str,
11604    parameter_names: &[String],
11605    ancestry: &ParentIndex<'tree>,
11606) -> Option<CppTemplateExpression> {
11607    let default = cpp_template_parameter_default(parameter)?;
11608    let base = cpp_template_expression(default, source, parameter_names, ancestry);
11609    let Some(pointer_error) = parameter.next_named_sibling() else {
11610        return Some(base);
11611    };
11612    let Some(pointer_declarator) =
11613        recovered_abstract_pointer_declarator_term(pointer_error, source)
11614    else {
11615        return Some(base);
11616    };
11617    Some(CppTemplateExpression {
11618        text: format!(
11619            "{}{}",
11620            base.text,
11621            normalize_cpp_whitespace(node_text(pointer_error, source))
11622        ),
11623        term: CppTemplateTerm::Node {
11624            kind: "type_descriptor".to_string(),
11625            children: vec![base.term, pointer_declarator],
11626        },
11627    })
11628}
11629
11630fn recovered_abstract_pointer_declarator_term(
11631    node: Node<'_>,
11632    source: &str,
11633) -> Option<CppTemplateTerm> {
11634    if node.kind() != "ERROR" || node.child_count() == 0 {
11635        return None;
11636    }
11637    let mut children = Vec::new();
11638    for index in 0..node.child_count() {
11639        let child = node.child(index)?;
11640        if child.kind() != "*" {
11641            return None;
11642        }
11643        children.push(CppTemplateTerm::Atom {
11644            kind: "*".to_string(),
11645            text: normalize_cpp_whitespace(node_text(child, source)),
11646        });
11647    }
11648    Some(CppTemplateTerm::Node {
11649        kind: "abstract_pointer_declarator".to_string(),
11650        children,
11651    })
11652}
11653
11654fn cpp_template_expression<'tree>(
11655    node: Node<'tree>,
11656    source: &str,
11657    parameter_names: &[String],
11658    ancestry: &ParentIndex<'tree>,
11659) -> CppTemplateExpression {
11660    let text = normalize_cpp_whitespace(node_text(node, source));
11661    CppTemplateExpression {
11662        text,
11663        term: cpp_template_term(node, source, parameter_names, ancestry),
11664    }
11665}
11666
11667pub fn cpp_template_term<'tree>(
11668    node: Node<'tree>,
11669    source: &str,
11670    parameter_names: &[String],
11671    ancestry: &ParentIndex<'tree>,
11672) -> CppTemplateTerm {
11673    enum Work<'tree> {
11674        Visit(Node<'tree>),
11675        Build { kind: String, child_count: usize },
11676    }
11677
11678    let mut work = vec![Work::Visit(node)];
11679    let mut terms = Vec::new();
11680    while let Some(next) = work.pop() {
11681        match next {
11682            Work::Visit(current) => {
11683                let text = normalize_cpp_whitespace(node_text(current, source));
11684                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
11685                    terms.push(CppTemplateTerm::Parameter(text));
11686                    continue;
11687                }
11688                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
11689                    let mut cursor = current.walk();
11690                    let named = current
11691                        .named_children(&mut cursor)
11692                        .filter(|child| !child.is_extra() && child.kind() != "comment")
11693                        .collect::<Vec<_>>();
11694                    if let [child] = named.as_slice() {
11695                        work.push(Work::Visit(*child));
11696                        continue;
11697                    }
11698                }
11699                if current.child_count() == 0 {
11700                    terms.push(CppTemplateTerm::Atom {
11701                        kind: if matches!(
11702                            current.kind(),
11703                            "identifier"
11704                                | "type_identifier"
11705                                | "field_identifier"
11706                                | "namespace_identifier"
11707                        ) {
11708                            "identifier".to_string()
11709                        } else {
11710                            current.kind().to_string()
11711                        },
11712                        text,
11713                    });
11714                    continue;
11715                }
11716                let children = (0..current.child_count())
11717                    .filter_map(|index| current.child(index))
11718                    .filter(|child| !child.is_extra() && child.kind() != "comment")
11719                    .collect::<Vec<_>>();
11720                work.push(Work::Build {
11721                    kind: current.kind().to_string(),
11722                    child_count: children.len(),
11723                });
11724                work.extend(children.into_iter().rev().map(Work::Visit));
11725            }
11726            Work::Build { kind, child_count } => {
11727                let children = terms.split_off(terms.len() - child_count);
11728                terms.push(CppTemplateTerm::Node { kind, children });
11729            }
11730        }
11731    }
11732    terms.pop().expect("template term traversal emits one root")
11733}
11734
11735fn cpp_template_term_leaf_is_parameter<'tree>(
11736    node: Node<'tree>,
11737    text: &str,
11738    parameter_names: &[String],
11739    ancestry: &ParentIndex<'tree>,
11740) -> bool {
11741    if !parameter_names.iter().any(|parameter| parameter == text) {
11742        return false;
11743    }
11744    !ancestry.parent(node).is_some_and(|parent| {
11745        matches!(
11746            parent.kind(),
11747            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
11748        ) && parent.child_by_field_name("scope").is_some()
11749            && parent.child_by_field_name("name") == Some(node)
11750    })
11751}
11752
11753fn enclosing_cpp_declaration_node<'tree>(
11754    mut node: Node<'tree>,
11755    ancestry: &ParentIndex<'tree>,
11756) -> Option<Node<'tree>> {
11757    loop {
11758        match node.kind() {
11759            "declaration"
11760            | "function_declaration"
11761            | "field_declaration"
11762            | "function_definition" => return Some(node),
11763            _ => node = ancestry.parent(node)?,
11764        }
11765    }
11766}
11767
11768fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
11769    let mut params = Vec::new();
11770    let mut cursor = parameters_node.walk();
11771    for child in parameters_node.children(&mut cursor) {
11772        match child.kind() {
11773            "parameter_declaration" | "optional_parameter_declaration" => {
11774                params.push(cpp_parameter_type(child, source));
11775            }
11776            "variadic_parameter_declaration" => {
11777                params.push(cpp_parameter_type(child, source));
11778            }
11779            "variadic_parameter" | "..." => params.push("...".to_string()),
11780            _ => {}
11781        }
11782    }
11783
11784    if params.is_empty() {
11785        "()".to_string()
11786    } else {
11787        format!("({})", params.join(", "))
11788    }
11789}
11790
11791/// Where a C or C++ callable declaration is written, which is what decides
11792/// which modifier facts its own node is able to state.
11793enum CppCallableScope<'tree> {
11794    /// Written directly in a class, struct or union body. The declaration
11795    /// states its own storage class, and its access is the one the body's
11796    /// specifier ladder is under at that point.
11797    ClassBody {
11798        body: Node<'tree>,
11799        default_visibility: DeclaredVisibility,
11800    },
11801    /// A qualified declaration written outside every class body
11802    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
11803    /// writes no access specifier, and whether the qualifier names a class or
11804    /// a namespace is not decidable from this file alone -- that is exactly
11805    /// what [`crate::reconcile`] needs the include-visible class table for.
11806    Qualified,
11807    /// Namespace scope or file scope, reached by an unqualified name. C has no
11808    /// member functions at all, so every C callable lands here.
11809    Unqualified,
11810}
11811
11812/// Where the declaration sits, from one upward walk of its own ancestry.
11813///
11814/// The walk stops as soon as something proves the declaration is outside every
11815/// class body: a `namespace_definition`, because a class body never encloses
11816/// one, and a `compound_statement`, because a body opens a block scope and
11817/// nothing declared in it is a member of the class the enclosing definition
11818/// belongs to. That is the same rule
11819/// `CppVisitor::visit_anonymous_local_aggregates_in_function` applies when it
11820/// drops the aggregate carrier, and it keeps this question off the O(depth)
11821/// walk a deeply nested generated header would otherwise pay per callable.
11822///
11823/// `function_declarator` is `None` for a declaration whose callable the macro
11824/// recovery reparsed out of a malformed member: there is no declarator node to
11825/// ask whether the name carries a qualifier, and a recovered member is written
11826/// in its class body anyway.
11827fn cpp_callable_scope<'tree>(
11828    declaration: Node<'tree>,
11829    function_declarator: Option<Node<'tree>>,
11830    ancestry: &ParentIndex<'tree>,
11831) -> CppCallableScope<'tree> {
11832    let mut current = Some(declaration);
11833    while let Some(node) = current {
11834        match node.kind() {
11835            "field_declaration_list" => {
11836                let default_visibility =
11837                    ancestry
11838                        .parent(node)
11839                        .map_or(DeclaredVisibility::Unknown, |class_like| {
11840                            // `struct` and `union` members default to public;
11841                            // `class` members default to private.
11842                            match class_like.kind() {
11843                                "struct_specifier" | "union_specifier" => {
11844                                    DeclaredVisibility::Public
11845                                }
11846                                _ => DeclaredVisibility::Private,
11847                            }
11848                        });
11849                return CppCallableScope::ClassBody {
11850                    body: node,
11851                    default_visibility,
11852                };
11853            }
11854            "namespace_definition" | "compound_statement" | "translation_unit" => break,
11855            _ => {}
11856        }
11857        current = ancestry.parent(node);
11858    }
11859    if cpp_callable_declarator_is_qualified(function_declarator) {
11860        CppCallableScope::Qualified
11861    } else {
11862        CppCallableScope::Unqualified
11863    }
11864}
11865
11866/// Whether the declarator's own written name carries a `::` qualifier.
11867///
11868/// A qualified declaration written outside every class body
11869/// (`long Client::send(int) {}`) names a member whose class body this file may
11870/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
11871/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
11872fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
11873    function_declarator
11874        .and_then(cpp_function_declarator_name_node)
11875        .is_some_and(|name| name.kind() == "qualified_identifier")
11876}
11877
11878/// The access one class-body member declaration is written under.
11879///
11880/// C++ states member access positionally: an `access_specifier` applies to
11881/// every member written after it until the next one, and the members before
11882/// the first one take the class keyword's default. The answer is therefore the
11883/// last specifier the body writes before the member's own list-level node,
11884/// read from the specifier's own keyword token rather than from its text.
11885///
11886/// The body's children are read forward with one cursor rather than backwards
11887/// from the member: `Node::prev_sibling` re-descends from the root for each
11888/// step, which is the per-edge cost #2361 keeps off this walk.
11889fn cpp_member_declared_visibility<'tree>(
11890    declaration: Node<'tree>,
11891    body: Node<'tree>,
11892    default_visibility: DeclaredVisibility,
11893    ancestry: &ParentIndex<'tree>,
11894) -> DeclaredVisibility {
11895    let mut list_level = declaration;
11896    while ancestry.parent(list_level) != Some(body) {
11897        let Some(parent) = ancestry.parent(list_level) else {
11898            return default_visibility;
11899        };
11900        list_level = parent;
11901    }
11902    let mut visibility = default_visibility;
11903    let mut cursor = body.walk();
11904    for child in body.children(&mut cursor) {
11905        if child == list_level {
11906            break;
11907        }
11908        if child.kind() == "access_specifier" {
11909            visibility = match child.child(0).map(|keyword| keyword.kind()) {
11910                Some("public") => DeclaredVisibility::Public,
11911                Some("protected") => DeclaredVisibility::Protected,
11912                Some("private") => DeclaredVisibility::Private,
11913                _ => visibility,
11914            };
11915        }
11916    }
11917    visibility
11918}
11919
11920/// Whether this declaration writes the `static` storage class specifier.
11921///
11922/// What `static` *means* is the scope's business: on a class-body member it is
11923/// the receiver fact, and at file scope it is internal linkage, which is why
11924/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
11925/// question and answer it differently.
11926fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
11927    let mut cursor = declaration.walk();
11928    declaration.named_children(&mut cursor).any(|child| {
11929        child.kind() == "storage_class_specifier"
11930            && normalize_cpp_whitespace(node_text(child, source)) == "static"
11931    })
11932}
11933
11934/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
11935/// publishes, as C and C++ state them.
11936struct CppCallableModifiers {
11937    is_static: bool,
11938    is_constructor: bool,
11939    visibility: DeclaredVisibility,
11940}
11941
11942/// The modifier facts a C or C++ callable declaration states in its own shape.
11943///
11944/// C++ has no `static` keyword for receivers and no visibility keyword of the
11945/// Java kind: it states both in the declaration's position. A member declared
11946/// in a class body binds the object it is selected on unless that declaration
11947/// writes the `static` storage class, and its access is the one the body's
11948/// specifier ladder is under. A function at namespace or file scope binds
11949/// nothing and has no access specifier at all, so its declared visibility is
11950/// Unknown. Linkage is recorded separately and does not declare access. C has
11951/// no member functions, and the `static` a C function writes is internal linkage,
11952/// never a receiver fact.
11953///
11954/// Recording these is what lets `receiver_contract_of` answer for a C or C++
11955/// declaration. Without them it reported no contract at all,
11956/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
11957/// callable, no reviewed summary could name one, and every effect or taint walk
11958/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
11959/// in #3455, Kotlin in #3453, Python in #3451.
11960///
11961/// An out-of-line member definition cannot repeat `static` or access. These
11962/// are per-file facts only: the analyzer reconciles the exact overload with
11963/// include-visible class declarations before exposing its receiver contract
11964/// (#3509). A missing declaration leaves that contract unknown.
11965fn cpp_callable_modifiers<'tree>(
11966    declaration: Node<'tree>,
11967    function_declarator: Option<Node<'tree>>,
11968    is_constructor: bool,
11969    source: &str,
11970    ancestry: &ParentIndex<'tree>,
11971) -> CppCallableModifiers {
11972    match cpp_callable_scope(declaration, function_declarator, ancestry) {
11973        CppCallableScope::ClassBody {
11974            body,
11975            default_visibility,
11976        } => CppCallableModifiers {
11977            is_static: cpp_declares_static_storage_class(declaration, source),
11978            is_constructor,
11979            visibility: cpp_member_declared_visibility(
11980                declaration,
11981                body,
11982                default_visibility,
11983                ancestry,
11984            ),
11985        },
11986        CppCallableScope::Qualified => CppCallableModifiers {
11987            is_static: false,
11988            is_constructor,
11989            visibility: DeclaredVisibility::Unknown,
11990        },
11991        CppCallableScope::Unqualified => CppCallableModifiers {
11992            is_static: false,
11993            is_constructor,
11994            visibility: DeclaredVisibility::Unknown,
11995        },
11996    }
11997}
11998
11999/// The persisted signature facts one C or C++ callable declaration publishes.
12000///
12001/// `linkage` is the caller's, not this function's: the node that carries the
12002/// storage class is not always the declaration this declarator sits in -- a
12003/// recovered constructor reads it from its class declaration, and a
12004/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
12005/// visit site that knows which node states it passes the answer in. Recording
12006/// it here keeps it beside the declared visibility it also decides.
12007fn cpp_signature_metadata<'tree>(
12008    signature: String,
12009    function_declarator: Node<'tree>,
12010    linkage: CallableLinkage,
12011    source: &str,
12012    ancestry: &ParentIndex<'tree>,
12013) -> SignatureMetadata {
12014    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
12015    let is_constructor =
12016        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
12017    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
12018        Some(declaration) => cpp_callable_modifiers(
12019            declaration,
12020            Some(function_declarator),
12021            is_constructor,
12022            source,
12023            ancestry,
12024        ),
12025        // Severe recovery left no declaration node above this declarator, so
12026        // nothing is stated here beyond what the callable's own name proves.
12027        None => CppCallableModifiers {
12028            is_static: false,
12029            is_constructor,
12030            visibility: DeclaredVisibility::Unknown,
12031        },
12032    };
12033    let enrich = |mut metadata: SignatureMetadata| {
12034        // #3507: a qualified definition repeats none of the class body's
12035        // dispatch facts, so this row publishes none either.
12036        if let Some(dispatch) = dispatch {
12037            metadata = metadata.with_dispatch_extensibility(dispatch);
12038        }
12039        metadata
12040            .with_callable_linkage(linkage)
12041            .with_callable_modifiers(
12042                modifiers.is_static,
12043                modifiers.is_constructor,
12044                modifiers.visibility,
12045            )
12046    };
12047    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
12048    let return_type_identity =
12049        cpp_callable_return_type_identity(function_declarator, source, ancestry);
12050    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12051        return enrich(
12052            SignatureMetadata::new(signature, Vec::new())
12053                .with_return_type_text(return_type_text)
12054                .with_return_type_identity(return_type_identity),
12055        );
12056    };
12057    let callable_arity = cpp_callable_arity(parameters_node, source);
12058    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
12059    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
12060    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
12061    let Some(relative_start) = signature
12062        .get(search_from..)
12063        .and_then(|suffix| suffix.find(&parameter_text))
12064    else {
12065        return enrich(
12066            SignatureMetadata::new(signature, Vec::new())
12067                .with_callable_arity(callable_arity)
12068                .with_callable_parameter_types(callable_parameter_types)
12069                .with_return_type_text(return_type_text)
12070                .with_return_type_identity(return_type_identity),
12071        );
12072    };
12073    let parameters_start = search_from + relative_start;
12074    let parameters_end = parameters_start + parameter_text.len();
12075    let mut search_start = parameters_start;
12076    let parameters = cpp_parameter_label_nodes(parameters_node)
12077        .into_iter()
12078        .filter_map(|label_node| {
12079            let label = normalize_cpp_whitespace(node_text(label_node, source));
12080            if label.is_empty() || search_start > parameters_end {
12081                return None;
12082            }
12083            let haystack = signature.get(search_start..parameters_end)?;
12084            let relative_start = haystack.find(&label)?;
12085            let start_byte = search_start + relative_start;
12086            let end_byte = start_byte + label.len();
12087            search_start = end_byte;
12088            Some(ParameterMetadata::new(label, start_byte, end_byte))
12089        })
12090        .collect();
12091    enrich(
12092        SignatureMetadata::new(signature, parameters)
12093            .with_callable_arity(callable_arity)
12094            .with_callable_parameter_types(callable_parameter_types)
12095            .with_return_type_text(return_type_text)
12096            .with_return_type_identity(return_type_identity),
12097    )
12098}
12099
12100fn cpp_callable_is_structural_constructor<'tree>(
12101    function_declarator: Node<'tree>,
12102    source: &str,
12103    ancestry: &ParentIndex<'tree>,
12104) -> bool {
12105    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
12106        .or_else(|| function_declarator.child_by_field_name("name"))
12107        .or_else(|| last_named_child(function_declarator))
12108    else {
12109        return false;
12110    };
12111    // An out-of-line definition writes the class in the name itself
12112    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
12113    // terminal component answers without any enclosing class body.
12114    if name_node.kind() == "qualified_identifier" {
12115        let owner = name_node
12116            .child_by_field_name("scope")
12117            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
12118        let member = name_node
12119            .child_by_field_name("name")
12120            .and_then(|name| canonical_cpp_qualified_component(name, source));
12121        return match (owner, member) {
12122            (Some(owner), Some(member)) => owner.name == member.name,
12123            _ => false,
12124        };
12125    }
12126    let Some(callable_name) = direct_identifier_name(name_node, source) else {
12127        return false;
12128    };
12129
12130    let mut current = ancestry.parent(function_declarator);
12131    while let Some(ancestor) = current {
12132        let owner_name = match ancestor.kind() {
12133            "class_specifier" | "struct_specifier" | "union_specifier" => {
12134                class_like_name(ancestor, source, ancestry)
12135            }
12136            "ERROR" => malformed_class_error_owner_name(ancestor, source),
12137            _ => None,
12138        };
12139        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
12140            return true;
12141        }
12142        current = ancestry.parent(ancestor);
12143    }
12144    false
12145}
12146
12147/// Recover the owner name from the direct grammar shape retained when a later
12148/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
12149/// `ERROR` node:
12150///
12151/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
12152///
12153/// Direct-child checks keep this distinct from an unrelated nested class inside
12154/// a broader error region. The closing brace may be displaced past the error
12155/// node, so the opening body token is the available structural boundary.
12156fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
12157    if node.kind() != "ERROR" {
12158        return None;
12159    }
12160    let keyword = node.child(0)?;
12161    if !matches!(keyword.kind(), "class" | "struct" | "union") {
12162        return None;
12163    }
12164    let name_node = node.child(1)?;
12165    let name = direct_identifier_name(name_node, source)?;
12166    let has_body = (2..node.child_count())
12167        .filter_map(|index| node.child(index))
12168        .any(|child| child.kind() == "{");
12169    has_body.then_some(name)
12170}
12171
12172/// The parser-derived return type of one callable declaration.
12173///
12174/// This accepts the enclosing declaration node so consumers do not need to
12175/// duplicate the declarator-unwrapping rules before asking for the structured
12176/// identity.
12177pub fn cpp_callable_declaration_return_type_identity<'tree>(
12178    callable: Node<'tree>,
12179    source: &str,
12180    ancestry: &ParentIndex<'tree>,
12181) -> Option<StructuredTypeIdentity> {
12182    let declarator = callable
12183        .child_by_field_name("declarator")
12184        .and_then(extract_function_declarator)?;
12185    cpp_callable_return_type_identity(declarator, source, ancestry)
12186}
12187
12188pub(crate) fn cpp_callable_return_type_identity<'tree>(
12189    function_declarator: Node<'tree>,
12190    source: &str,
12191    ancestry: &ParentIndex<'tree>,
12192) -> Option<StructuredTypeIdentity> {
12193    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12194        return None;
12195    }
12196    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12197    if let Some((return_type, _)) =
12198        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12199    {
12200        return cpp_structured_type_identity(return_type, source, &lexical_scope);
12201    }
12202    let mut cursor = function_declarator.walk();
12203    if let Some(trailing) = function_declarator
12204        .named_children(&mut cursor)
12205        .find(|child| child.kind() == "trailing_return_type")
12206        && let Some(type_descriptor) = trailing.named_child(0)
12207    {
12208        return cpp_structured_type_identity(type_descriptor, source, &lexical_scope);
12209    }
12210
12211    let mut current = function_declarator;
12212    let mut wrappers = Vec::new();
12213    while let Some(parent) = ancestry.parent(current) {
12214        if matches!(
12215            parent.kind(),
12216            "function_definition" | "declaration" | "field_declaration"
12217        ) {
12218            let type_node = parent.child_by_field_name("type")?;
12219            if cpp_export_macro_token(node_text(type_node, source))
12220                && (0..parent.named_child_count()).any(|index| {
12221                    parent
12222                        .named_child(index)
12223                        .is_some_and(|child| child.kind() == "ERROR")
12224                })
12225            {
12226                return None;
12227            }
12228            let mut identity = cpp_structured_type_identity(type_node, source, &lexical_scope)?;
12229            for wrapper in wrappers.into_iter().rev() {
12230                identity = cpp_wrap_structured_type(identity, wrapper)?;
12231            }
12232            return Some(identity);
12233        }
12234        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12235            || (matches!(
12236                parent.kind(),
12237                "pointer_declarator"
12238                    | "reference_declarator"
12239                    | "array_declarator"
12240                    | "parenthesized_declarator"
12241            ) && parent.named_child_count() == 1
12242                && parent.named_child(0) == Some(current));
12243        if !wraps_current_declarator {
12244            return None;
12245        }
12246        match parent.kind() {
12247            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
12248            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
12249            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
12250            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
12251            _ => return None,
12252        }
12253        current = parent;
12254    }
12255    None
12256}
12257
12258fn cpp_structured_type_identity(
12259    node: Node<'_>,
12260    source: &str,
12261    lexical_scope: &[String],
12262) -> Option<StructuredTypeIdentity> {
12263    enum Work<'tree> {
12264        Visit(Node<'tree>),
12265        Wrap(CppStructuredTypeWrapper),
12266        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
12267        BuildGeneric { argument_count: usize },
12268    }
12269
12270    let mut work = vec![Work::Visit(node)];
12271    let mut values = Vec::new();
12272    let mut builder = StructuredTypeIdentityBuilder::default();
12273    while let Some(next) = work.pop() {
12274        match next {
12275            Work::Visit(current) => match current.kind() {
12276                "type_descriptor" => {
12277                    let type_node = current
12278                        .child_by_field_name("type")
12279                        .or_else(|| current.named_child(0))?;
12280                    let mut wrappers = Vec::new();
12281                    let mut cursor = current.walk();
12282                    for child in current.named_children(&mut cursor) {
12283                        if child.id() != type_node.id() {
12284                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
12285                        }
12286                    }
12287                    work.push(Work::ApplyWrappers(wrappers));
12288                    work.push(Work::Visit(type_node));
12289                }
12290                "pointer_declarator" | "abstract_pointer_declarator" => {
12291                    let child = current
12292                        .child_by_field_name("declarator")
12293                        .or_else(|| current.named_child(0))?;
12294                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
12295                    work.push(Work::Visit(child));
12296                }
12297                "reference_declarator" => {
12298                    let child = current
12299                        .child_by_field_name("declarator")
12300                        .or_else(|| current.named_child(0))?;
12301                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
12302                    work.push(Work::Visit(child));
12303                }
12304                "array_declarator" | "abstract_array_declarator" => {
12305                    let child = current
12306                        .child_by_field_name("declarator")
12307                        .or_else(|| current.named_child(0))?;
12308                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
12309                    work.push(Work::Visit(child));
12310                }
12311                "template_type" => {
12312                    let name_node = current.child_by_field_name("name")?;
12313                    let arguments = current
12314                        .child_by_field_name("arguments")
12315                        .map(|arguments_node| {
12316                            let mut cursor = arguments_node.walk();
12317                            arguments_node
12318                                .named_children(&mut cursor)
12319                                .filter(|child| !child.is_extra() && child.kind() != "comment")
12320                                .collect::<Vec<_>>()
12321                        })
12322                        .unwrap_or_default();
12323                    work.push(Work::BuildGeneric {
12324                        argument_count: arguments.len(),
12325                    });
12326                    work.extend(arguments.into_iter().rev().map(Work::Visit));
12327                    work.push(Work::Visit(name_node));
12328                }
12329                "qualified_identifier"
12330                | "scoped_identifier"
12331                | "scoped_type_identifier"
12332                | "type_identifier"
12333                | "field_identifier"
12334                | "identifier"
12335                | "namespace_identifier"
12336                | "primitive_type" => {
12337                    values.push(builder.named(cpp_structured_named_type(
12338                        current,
12339                        source,
12340                        lexical_scope,
12341                    )?)?);
12342                }
12343                _ => {
12344                    let child = current.child_by_field_name("type").or_else(|| {
12345                        (current.named_child_count() == 1)
12346                            .then(|| current.named_child(0))
12347                            .flatten()
12348                    })?;
12349                    work.push(Work::Visit(child));
12350                }
12351            },
12352            Work::Wrap(wrapper) => {
12353                let root = values.pop()?;
12354                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
12355            }
12356            Work::ApplyWrappers(wrappers) => {
12357                let mut root = values.pop()?;
12358                for wrapper in wrappers.into_iter().rev() {
12359                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
12360                }
12361                values.push(root);
12362            }
12363            Work::BuildGeneric { argument_count } => {
12364                let value_count = argument_count.checked_add(1)?;
12365                let start = values.len().checked_sub(value_count)?;
12366                let mut built = values.split_off(start);
12367                let base = built.remove(0);
12368                values.push(builder.generic(base, built)?);
12369            }
12370        }
12371    }
12372    (values.len() == 1)
12373        .then(|| values.pop())
12374        .flatten()
12375        .and_then(|root| builder.finish(root))
12376}
12377
12378fn cpp_structured_named_type(
12379    node: Node<'_>,
12380    source: &str,
12381    lexical_scope: &[String],
12382) -> Option<StructuredTypeName> {
12383    let path = cpp_structured_type_path(node, source)?;
12384    let absolute = node.child_by_field_name("scope").is_none()
12385        && node.child(0).is_some_and(|child| child.kind() == "::");
12386    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
12387}
12388
12389#[derive(Clone, Copy)]
12390enum CppStructuredTypeWrapper {
12391    Pointer,
12392    LvalueReference,
12393    RvalueReference,
12394    Array,
12395}
12396
12397fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
12398    let mut wrappers = Vec::new();
12399    let mut current = node;
12400    loop {
12401        match current.kind() {
12402            "pointer_declarator" | "abstract_pointer_declarator" => {
12403                wrappers.push(CppStructuredTypeWrapper::Pointer)
12404            }
12405            "reference_declarator" | "abstract_reference_declarator" => {
12406                wrappers.push(cpp_reference_wrapper(current)?);
12407            }
12408            "array_declarator" | "abstract_array_declarator" => {
12409                wrappers.push(CppStructuredTypeWrapper::Array)
12410            }
12411            // An initializer and a grouping parenthesis are not type
12412            // structure. Stopping at one of them dropped every wrapper an
12413            // initialized or parenthesized declarator writes, so
12414            // `std::string &alias = value;` reported the unqualified
12415            // `std::string` while `const std::string &value` as a parameter
12416            // reported the reference it shares.
12417            "init_declarator" | "parenthesized_declarator" => {}
12418            _ => break,
12419        }
12420        let Some(child) = current
12421            .child_by_field_name("declarator")
12422            .or_else(|| current.named_child(0))
12423        else {
12424            break;
12425        };
12426        current = child;
12427    }
12428    Some(wrappers)
12429}
12430
12431fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
12432    node.children(&mut node.walk())
12433        .find_map(|child| match child.kind() {
12434            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
12435            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
12436            _ => None,
12437        })
12438}
12439
12440fn cpp_wrap_structured_type(
12441    identity: StructuredTypeIdentity,
12442    wrapper: CppStructuredTypeWrapper,
12443) -> Option<StructuredTypeIdentity> {
12444    match wrapper {
12445        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
12446        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
12447        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
12448        CppStructuredTypeWrapper::Array => identity.wrap_array(),
12449    }
12450}
12451
12452fn cpp_wrap_structured_type_node(
12453    builder: &mut StructuredTypeIdentityBuilder,
12454    inner: StructuredTypeNodeId,
12455    wrapper: CppStructuredTypeWrapper,
12456) -> Option<StructuredTypeNodeId> {
12457    match wrapper {
12458        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
12459        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
12460        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
12461        CppStructuredTypeWrapper::Array => builder.array(inner),
12462    }
12463}
12464
12465/// The dot-free name components a qualified C++ name node writes, outermost
12466/// first.
12467///
12468/// The walk reads the grammar's `scope` and `name` fields rather than cutting
12469/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
12470/// shape the grammar does not name yields nothing at all.
12471pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
12472    let mut path = Vec::new();
12473    let mut stack = vec![node];
12474    while let Some(current) = stack.pop() {
12475        match current.kind() {
12476            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
12477                let component = node_text(current, source).to_string();
12478                if component.is_empty() {
12479                    return None;
12480                }
12481                path.push(component);
12482            }
12483            "template_type" | "dependent_type" => {
12484                stack.push(current.child_by_field_name("name")?);
12485            }
12486            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
12487                stack.push(current.child_by_field_name("name")?);
12488                if let Some(scope) = current.child_by_field_name("scope") {
12489                    stack.push(scope);
12490                }
12491            }
12492            _ => return None,
12493        }
12494    }
12495    (!path.is_empty()).then_some(path)
12496}
12497
12498fn cpp_callable_lexical_scope<'tree>(
12499    node: Node<'tree>,
12500    source: &str,
12501    ancestry: &ParentIndex<'tree>,
12502) -> Vec<String> {
12503    let mut groups = Vec::new();
12504    let mut current = ancestry.parent(node);
12505    while let Some(parent) = current {
12506        if matches!(
12507            parent.kind(),
12508            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
12509        ) && let Some(name_node) = parent.child_by_field_name("name")
12510            && let Some(components) = cpp_structured_type_path(name_node, source)
12511            && !components.is_empty()
12512        {
12513            groups.push(components);
12514        }
12515        current = ancestry.parent(parent);
12516    }
12517    groups.reverse();
12518    groups.into_iter().flatten().collect()
12519}
12520
12521/// Whether a call on this callable's static target can select another body.
12522///
12523/// C++ says this where the member is *declared*, not where it is defined, and
12524/// that is the whole difficulty of the question: virtual-ness is inherited
12525/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
12526/// `Base::run` is), an out-of-line definition repeats none of it, and whether
12527/// a definition's qualifier names a class or a namespace is a class-table
12528/// question. So a row answers `Closed` only when its own node proves it:
12529///
12530/// * a written `final` is the one specifier that ends the family here, and it
12531///   is the only thing that closes a member of a class that has a base clause;
12532/// * a class-body member is `Closed` otherwise only when its class body writes
12533///   no base clause at all, because a base clause is the only evidence in this
12534///   file that some base may declare this member virtual;
12535/// * anything with a virtual-family boundary, a template head, or a
12536///   preprocessor or error region is `Open`;
12537/// * a qualified declaration outside every class body answers `None`: its
12538///   class body, which is where the fact is written, may be in another file.
12539///
12540/// The analyzer joins the `None` rows with the include-visible class-body rows
12541/// of the same logical callable (#3507), the same join #3509 makes for a
12542/// definition's declared access; a row that keeps `None` after that join stays
12543/// unknown and no dispatch proof may be built on it.
12544fn cpp_callable_dispatch_extensibility<'tree>(
12545    function_declarator: Node<'tree>,
12546    ancestry: &ParentIndex<'tree>,
12547) -> Option<DispatchExtensibility> {
12548    let mut declaration = None;
12549    // The first class body this declarator is written in. The upward walk
12550    // visits it anyway, so only the one step from the body to its class node
12551    // is extra, and only for a class-body member.
12552    let mut class_body = None;
12553    let mut current = Some(function_declarator);
12554    while let Some(node) = current {
12555        match node.kind() {
12556            "template_declaration"
12557            | "preproc_if"
12558            | "preproc_ifdef"
12559            | "preproc_else"
12560            | "preproc_elif"
12561            | "preproc_call"
12562            | "ERROR" => return Some(DispatchExtensibility::Open),
12563            "declaration" | "field_declaration" | "function_definition" => {
12564                declaration.get_or_insert(node);
12565            }
12566            "field_declaration_list" => {
12567                class_body.get_or_insert(node);
12568            }
12569            "translation_unit" => break,
12570            _ => {}
12571        }
12572        current = ancestry.parent(node);
12573    }
12574    let Some(declaration) = declaration else {
12575        return Some(DispatchExtensibility::Open);
12576    };
12577
12578    let mut saw_virtual_boundary = false;
12579    let mut stack = vec![declaration];
12580    while let Some(node) = stack.pop() {
12581        match node.kind() {
12582            "compound_statement" | "field_declaration_list" => continue,
12583            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
12584            "virtual"
12585            | "override"
12586            | "virtual_specifier"
12587            | "pure_virtual_clause"
12588            | "template_parameter_list"
12589            | "template_method"
12590            | "template_function"
12591            | "ERROR" => saw_virtual_boundary = true,
12592            _ => {}
12593        }
12594        let mut cursor = node.walk();
12595        stack.extend(node.children(&mut cursor));
12596    }
12597
12598    if saw_virtual_boundary {
12599        return Some(DispatchExtensibility::Open);
12600    }
12601    if let Some(body) = class_body {
12602        // C++ inherits virtual-ness, so a member written without a `virtual`
12603        // keyword is still virtual when a base declares it. Whether some base
12604        // does is not decidable here, so a base clause keeps the answer open.
12605        let has_base_clause = ancestry
12606            .parent(body)
12607            .and_then(cpp_class_like_base_clause)
12608            .is_some();
12609        return Some(if has_base_clause {
12610            DispatchExtensibility::Open
12611        } else {
12612            DispatchExtensibility::Closed
12613        });
12614    }
12615    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
12616        return None;
12617    }
12618    // A namespace or file scope callable is reached by name, never through a
12619    // receiver, so no other body can be selected for a call to it.
12620    Some(DispatchExtensibility::Closed)
12621}
12622
12623fn cpp_callable_linkage<'tree>(
12624    declaration: Node<'tree>,
12625    source: &str,
12626    ancestry: &ParentIndex<'tree>,
12627) -> CallableLinkage {
12628    let mut enclosed_by_class = false;
12629    let mut current = ancestry.parent(declaration);
12630    while let Some(node) = current {
12631        if node.kind() == "namespace_definition"
12632            && node
12633                .child_by_field_name("name")
12634                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12635        {
12636            return CallableLinkage::Internal;
12637        }
12638        if matches!(
12639            node.kind(),
12640            "class_specifier" | "struct_specifier" | "union_specifier"
12641        ) {
12642            if node
12643                .child_by_field_name("name")
12644                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
12645            {
12646                return CallableLinkage::Internal;
12647            }
12648            enclosed_by_class = true;
12649        }
12650        // An export macro between `class` and the class name can make
12651        // tree-sitter parse the entire class as a function definition. The
12652        // structured recovery proves that this container is a named class
12653        // scope, not a local callable scope.
12654        if node.kind() == "lambda_expression"
12655            || node.kind() == "function_definition"
12656                && !is_recovered_exported_class_container(node, source)
12657        {
12658            return CallableLinkage::Internal;
12659        }
12660        current = ancestry.parent(node);
12661    }
12662
12663    if enclosed_by_class {
12664        return CallableLinkage::External;
12665    }
12666
12667    if cpp_declares_static_storage_class(declaration, source) {
12668        CallableLinkage::Internal
12669    } else {
12670        CallableLinkage::External
12671    }
12672}
12673
12674fn cpp_callable_return_type_text<'tree>(
12675    function_declarator: Node<'tree>,
12676    source: &str,
12677    ancestry: &ParentIndex<'tree>,
12678) -> Option<String> {
12679    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
12680        return None;
12681    }
12682    if let Some((return_type, _)) =
12683        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
12684    {
12685        let text = normalize_cpp_whitespace(node_text(return_type, source));
12686        return (!text.is_empty()).then_some(text);
12687    }
12688    let mut cursor = function_declarator.walk();
12689    if let Some(trailing) = function_declarator
12690        .named_children(&mut cursor)
12691        .find(|child| child.kind() == "trailing_return_type")
12692        && let Some(type_descriptor) = trailing.named_child(0)
12693    {
12694        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
12695        if !text.is_empty() {
12696            return Some(text);
12697        }
12698    }
12699
12700    let mut current = function_declarator;
12701    let mut indirection = String::new();
12702    while let Some(parent) = ancestry.parent(current) {
12703        if matches!(
12704            parent.kind(),
12705            "function_definition" | "declaration" | "field_declaration"
12706        ) {
12707            let type_node = parent.child_by_field_name("type")?;
12708            if cpp_export_macro_token(node_text(type_node, source))
12709                && (0..parent.named_child_count()).any(|index| {
12710                    parent
12711                        .named_child(index)
12712                        .is_some_and(|child| child.kind() == "ERROR")
12713                })
12714            {
12715                // Export/decorator macros commonly occupy the grammar's `type`
12716                // field and leave the semantic return type in an ERROR sibling.
12717                // Do not persist the macro token as a return type. The malformed
12718                // declaration does not carry enough structured evidence here.
12719                return None;
12720            }
12721            let base = normalize_cpp_whitespace(node_text(type_node, source));
12722            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
12723        }
12724        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
12725            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
12726                && parent.named_child_count() == 1
12727                && parent.named_child(0) == Some(current));
12728        if wraps_current_declarator {
12729            match parent.kind() {
12730                "pointer_declarator" => indirection.push('*'),
12731                "reference_declarator" => {
12732                    let reference = parent
12733                        .children(&mut parent.walk())
12734                        .find(|child| !child.is_named())
12735                        .map(|child| node_text(child, source))
12736                        .unwrap_or("&");
12737                    indirection.push_str(reference);
12738                }
12739                "init_declarator" | "parenthesized_declarator" => {}
12740                _ => return None,
12741            }
12742            current = parent;
12743            continue;
12744        }
12745        return None;
12746    }
12747    None
12748}
12749
12750fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
12751    let mut required = 0;
12752    let mut total = 0;
12753    let mut repeated = false;
12754    let mut cursor = parameters_node.walk();
12755    for child in parameters_node.children(&mut cursor) {
12756        match child.kind() {
12757            "parameter_declaration" => {
12758                if cpp_parameter_is_explicit_object(child, source) {
12759                    continue;
12760                }
12761                if child.child_by_field_name("declarator").is_none()
12762                    && child
12763                        .child_by_field_name("type")
12764                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void")
12765                {
12766                    continue;
12767                }
12768                required += 1;
12769                total += 1;
12770            }
12771            "optional_parameter_declaration" => total += 1,
12772            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12773                repeated = true;
12774            }
12775            _ => {}
12776        }
12777    }
12778    CallableArity::new(required, total, repeated)
12779}
12780
12781fn cpp_parameter_is_explicit_object(parameter: Node<'_>, source: &str) -> bool {
12782    parameter
12783        .child_by_field_name("type")
12784        .filter(|type_node| type_node.kind() == "placeholder_type_specifier")
12785        .and_then(|type_node| type_node.child_by_field_name("constraint"))
12786        .is_some_and(|constraint| {
12787            constraint.kind() == "type_identifier" && node_text(constraint, source).trim() == "this"
12788        })
12789}
12790
12791/// One entry of a callable's invocation parameter list.
12792///
12793/// The list excludes an explicit object parameter and a lone `void`, so its
12794/// length is the callable's invocation arity. Every derivation of a parameter
12795/// type - the rendered spelling used for overload discrimination and the
12796/// structured identity used by dependency-pack production - starts from this
12797/// same sequence, so the two can never disagree about which parameters exist.
12798#[derive(Clone, Copy)]
12799enum CppParameterSlot<'tree> {
12800    Declared(Node<'tree>),
12801    Ellipsis,
12802}
12803
12804fn cpp_callable_parameter_slots<'tree>(
12805    parameters_node: Node<'tree>,
12806    source: &str,
12807) -> Vec<CppParameterSlot<'tree>> {
12808    let mut slots = Vec::new();
12809    let mut cursor = parameters_node.walk();
12810    for parameter in parameters_node.children(&mut cursor) {
12811        match parameter.kind() {
12812            "parameter_declaration" | "optional_parameter_declaration" => {
12813                if cpp_parameter_is_explicit_object(parameter, source)
12814                    || (parameter.child_by_field_name("declarator").is_none()
12815                        && parameter
12816                            .child_by_field_name("type")
12817                            .is_some_and(|type_node| node_text(type_node, source).trim() == "void"))
12818                {
12819                    continue;
12820                }
12821                slots.push(CppParameterSlot::Declared(parameter));
12822            }
12823            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
12824                slots.push(CppParameterSlot::Ellipsis);
12825            }
12826            _ => {}
12827        }
12828    }
12829    slots
12830}
12831
12832fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
12833    cpp_callable_parameter_slots(parameters_node, source)
12834        .into_iter()
12835        .map(|slot| match slot {
12836            CppParameterSlot::Declared(parameter) => cpp_parameter_type(parameter, source),
12837            CppParameterSlot::Ellipsis => "...".to_string(),
12838        })
12839        .collect()
12840}
12841
12842/// One callable parameter's parser-derived type.
12843///
12844/// A rendered spelling such as `const T&` is a source text, not a type name. A
12845/// consumer that must publish a type into a structured model - a semantic-pack
12846/// type reference, for example - reads this instead.
12847#[derive(Debug, Clone, PartialEq, Eq)]
12848pub enum CppParameterType {
12849    /// The written type reduced to a structured identity. C++ cv-qualifiers
12850    /// have no place in that model and are not represented.
12851    Structured(StructuredTypeIdentity),
12852    /// A `...` pack, which declares no parameter type at all.
12853    Ellipsis,
12854    /// A written type with no structured reduction, such as a macro-obscured,
12855    /// `decltype`-computed, or function-pointer parameter.
12856    Unstructured,
12857}
12858
12859/// The structured type of each invocation parameter, in declaration order.
12860///
12861/// The result is index-parallel with the rendered
12862/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
12863/// callable.
12864pub fn cpp_callable_parameter_type_identities<'tree>(
12865    function_declarator: Node<'tree>,
12866    source: &str,
12867    ancestry: &ParentIndex<'tree>,
12868) -> Vec<CppParameterType> {
12869    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
12870        return Vec::new();
12871    };
12872    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
12873    cpp_callable_parameter_slots(parameters_node, source)
12874        .into_iter()
12875        .map(|slot| match slot {
12876            CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
12877            CppParameterSlot::Declared(parameter) => {
12878                cpp_parameter_type_identity(parameter, source, &lexical_scope)
12879                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
12880            }
12881        })
12882        .collect()
12883}
12884
12885/// The parser-derived type of one declared object or parameter.
12886///
12887/// The declaration owns the base `type` field while the individual declarator
12888/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
12889/// lets callers distinguish several declarators in one declaration without
12890/// reparsing a rendered signature.
12891pub fn cpp_declaration_type_identity<'tree>(
12892    declaration: Node<'tree>,
12893    declarator: Node<'tree>,
12894    source: &str,
12895    ancestry: &ParentIndex<'tree>,
12896) -> Option<StructuredTypeIdentity> {
12897    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
12898    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
12899}
12900
12901fn cpp_parameter_type_identity(
12902    parameter: Node<'_>,
12903    source: &str,
12904    lexical_scope: &[String],
12905) -> Option<StructuredTypeIdentity> {
12906    cpp_declaration_type_identity_in_scope(
12907        parameter,
12908        cpp_parameter_declarator(parameter),
12909        source,
12910        lexical_scope,
12911    )
12912}
12913
12914fn cpp_declaration_type_identity_in_scope(
12915    declaration: Node<'_>,
12916    declarator: Option<Node<'_>>,
12917    source: &str,
12918    lexical_scope: &[String],
12919) -> Option<StructuredTypeIdentity> {
12920    let type_node = declaration.child_by_field_name("type")?;
12921    let mut identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
12922    if let Some(declarator) = declarator {
12923        for wrapper in cpp_structured_declarator_wrappers(declarator)?
12924            .into_iter()
12925            .rev()
12926        {
12927            identity = cpp_wrap_structured_type(identity, wrapper)?;
12928        }
12929    }
12930    Some(identity)
12931}
12932
12933/// One callable parameter's comparable shape.
12934///
12935/// [`CppParameterType`] above answers "which type is written here" for a
12936/// structured model and deliberately records no cv-qualifiers, so it reports
12937/// the same value for `f(char *)` and `f(const char *)`. Deciding whether two
12938/// callable declarations declare one function needs the opposite trade: every
12939/// cv-qualifier that C++ counts as part of the parameter type must survive,
12940/// while the two declarations may spell the same type through different
12941/// qualifications. This slot carries that comparand.
12942///
12943/// The result is index-parallel with [`cpp_callable_parameter_type_identities`]
12944/// and with the rendered parameter spellings of the same callable.
12945#[derive(Debug, Clone, PartialEq, Eq)]
12946pub enum CppComparableSlot {
12947    /// A declared parameter reduced to its comparable shape.
12948    Shape(CppComparableParameter),
12949    /// A `...` pack, which declares no parameter type at all.
12950    Ellipsis,
12951    /// A parameter with no comparable reduction, such as a macro-obscured,
12952    /// `decltype`-computed, or function-pointer parameter.
12953    Unstructured,
12954}
12955
12956/// A parameter type as a flat arena of nodes plus a root index.
12957///
12958/// The arena carries the same rationale as [`StructuredTypeIdentity`]: source
12959/// can nest types very deeply, and cloning, comparing or dropping the value
12960/// must not consume the Rust call stack. Nodes are appended in post-order, so
12961/// every child index is smaller than its parent's and the last appended node is
12962/// the root.
12963///
12964/// That post-order append is also what makes the derived `PartialEq` a correct
12965/// structural equality: the builder below is deterministic, so one type shape
12966/// has exactly one arena layout no matter which spelling produced it. Two
12967/// shapes are equal as values iff they are equal as type trees.
12968#[derive(Debug, Clone, PartialEq, Eq)]
12969pub struct CppComparableParameter {
12970    nodes: Vec<CppComparableNode>,
12971    root: usize,
12972}
12973
12974/// One node of a [`CppComparableParameter`] arena.
12975///
12976/// `Reference` and `Array` carry no qualifiers because the grammar writes none
12977/// on them: a reference cannot be cv-qualified in C++, and an array's
12978/// qualifiers belong to its element type. A cv-qualifier written on a generic
12979/// type (`const std::vector<int>`) is recorded on the generic's base leaf,
12980/// which is the only Named node the whole spelling produces.
12981#[derive(Debug, Clone, PartialEq, Eq)]
12982pub enum CppComparableNode {
12983    Named {
12984        name: StructuredTypeName,
12985        primitive: bool,
12986        konst: bool,
12987        volatil: bool,
12988    },
12989    Pointer {
12990        inner: usize,
12991        konst: bool,
12992        volatil: bool,
12993    },
12994    Reference {
12995        inner: usize,
12996    },
12997    Array {
12998        inner: usize,
12999    },
13000    Generic {
13001        base: usize,
13002        arguments: Vec<usize>,
13003    },
13004}
13005
13006impl CppComparableParameter {
13007    pub fn root(&self) -> usize {
13008        self.root
13009    }
13010
13011    pub fn node(&self, index: usize) -> &CppComparableNode {
13012        &self.nodes[index]
13013    }
13014
13015    /// Apply the [dcl.fct]/5 parameter-type adjustments, which hold at the
13016    /// parameter's top level only.
13017    ///
13018    /// A top-level cv-qualifier is discarded, so `f(const int)` and `f(int)`
13019    /// declare one function, and a top-level array type becomes a pointer to
13020    /// its element type, so `f(int[3])` and `f(int *)` do too. The outermost
13021    /// type constructor is this arena's root, which is why both adjustments
13022    /// are one match on it: cv on an inner pointer level, on a pointee, or on
13023    /// an array element keeps distinguishing the type, and an array behind a
13024    /// pointer or reference is not a top-level array.
13025    fn adjust_parameter_top_level(&mut self) {
13026        let root = self.root;
13027        match &mut self.nodes[root] {
13028            CppComparableNode::Named { konst, volatil, .. }
13029            | CppComparableNode::Pointer { konst, volatil, .. } => {
13030                *konst = false;
13031                *volatil = false;
13032            }
13033            CppComparableNode::Array { inner } => {
13034                let inner = *inner;
13035                self.nodes[root] = CppComparableNode::Pointer {
13036                    inner,
13037                    konst: false,
13038                    volatil: false,
13039                };
13040            }
13041            CppComparableNode::Generic { base, .. } => {
13042                let base = *base;
13043                let CppComparableNode::Named { konst, volatil, .. } = &mut self.nodes[base] else {
13044                    unreachable!("a comparable generic's base is always a named leaf");
13045                };
13046                *konst = false;
13047                *volatil = false;
13048            }
13049            CppComparableNode::Reference { .. } => {}
13050        }
13051    }
13052}
13053
13054/// The comparable shape of each invocation parameter, in declaration order.
13055///
13056/// The result is index-parallel with
13057/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
13058/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
13059/// must treat as evidence of nothing rather than as agreement.
13060pub fn cpp_comparable_parameter_shapes<'tree>(
13061    function_declarator: Node<'tree>,
13062    source: &str,
13063    ancestry: &ParentIndex<'tree>,
13064) -> Vec<CppComparableSlot> {
13065    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
13066        return Vec::new();
13067    };
13068    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
13069    cpp_callable_parameter_slots(parameters_node, source)
13070        .into_iter()
13071        .map(|slot| match slot {
13072            CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
13073            CppParameterSlot::Declared(parameter) => {
13074                cpp_comparable_parameter(parameter, source, &lexical_scope)
13075                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
13076            }
13077        })
13078        .collect()
13079}
13080
13081fn cpp_comparable_parameter(
13082    parameter: Node<'_>,
13083    source: &str,
13084    lexical_scope: &[String],
13085) -> Option<CppComparableParameter> {
13086    let type_node = parameter.child_by_field_name("type")?;
13087    let levels = match cpp_parameter_declarator(parameter) {
13088        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
13089        None => Vec::new(),
13090    };
13091    let mut shape = cpp_comparable_type_shape(
13092        type_node,
13093        cpp_cv_qualifiers(parameter, source),
13094        levels,
13095        source,
13096        lexical_scope,
13097    )?;
13098    shape.adjust_parameter_top_level();
13099    Some(shape)
13100}
13101
13102/// The `const` and `volatile` qualifiers written as direct named children of
13103/// `node`.
13104///
13105/// The grammar exposes `type_qualifier` as a non-field named child in exactly
13106/// the three places a parameter's qualifiers can be written: on the
13107/// `parameter_declaration` itself (the base type), on a `type_descriptor`
13108/// (inside a template argument list), and on each `pointer_declarator` level
13109/// (the pointer object). Every other qualifier the grammar admits - `restrict`
13110/// and friends - takes no part in C++ type identity, the same filter
13111/// `cpp_parameter_type` applies to the rendered spelling (#1827).
13112fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
13113    let mut qualifiers = CppCvQualifiers::default();
13114    let mut cursor = node.walk();
13115    for child in node.named_children(&mut cursor) {
13116        if child.kind() != "type_qualifier" {
13117            continue;
13118        }
13119        match node_text(child, source) {
13120            "const" => qualifiers.konst = true,
13121            "volatile" => qualifiers.volatil = true,
13122            _ => {}
13123        }
13124    }
13125    qualifiers
13126}
13127
13128#[derive(Clone, Copy, Default)]
13129struct CppCvQualifiers {
13130    konst: bool,
13131    volatil: bool,
13132}
13133
13134impl CppCvQualifiers {
13135    fn union(self, other: Self) -> Self {
13136        Self {
13137            konst: self.konst || other.konst,
13138            volatil: self.volatil || other.volatil,
13139        }
13140    }
13141}
13142
13143/// One pointer, reference or array level a declarator chain adds.
13144#[derive(Clone, Copy)]
13145enum CppComparableLevel {
13146    Pointer { konst: bool, volatil: bool },
13147    Reference,
13148    Array,
13149}
13150
13151/// The levels `declarator` adds, outermost written level first.
13152///
13153/// C++ declarator syntax binds inside out: the level written closest to the
13154/// declared name is the outermost type constructor, and tree-sitter nests it
13155/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
13156/// applies in order to reach "array of pointer to int", and the qualifier of
13157/// `int * const *p` is read on the level it was written next to, the inner
13158/// pointer of the resulting type.
13159///
13160/// A declarator chain that names a function type - a function-pointer
13161/// parameter - has no comparable shape and reports `None`, matching the
13162/// structured identity channel.
13163fn cpp_comparable_declarator_levels(
13164    declarator: Node<'_>,
13165    source: &str,
13166) -> Option<Vec<CppComparableLevel>> {
13167    let mut levels = Vec::new();
13168    let mut current = declarator;
13169    loop {
13170        match current.kind() {
13171            "pointer_declarator" | "abstract_pointer_declarator" => {
13172                let qualifiers = cpp_cv_qualifiers(current, source);
13173                levels.push(CppComparableLevel::Pointer {
13174                    konst: qualifiers.konst,
13175                    volatil: qualifiers.volatil,
13176                });
13177            }
13178            "reference_declarator" | "abstract_reference_declarator" => {
13179                levels.push(CppComparableLevel::Reference);
13180            }
13181            "array_declarator" | "abstract_array_declarator" => {
13182                levels.push(CppComparableLevel::Array);
13183            }
13184            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
13185            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
13186            _ => return None,
13187        }
13188        let Some(next) = cpp_nested_declarator(current) else {
13189            return Some(levels);
13190        };
13191        current = next;
13192    }
13193}
13194
13195/// Reduce one written type to a comparable arena.
13196///
13197/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
13198/// two additions: each visited type node carries the cv-qualifiers written on
13199/// it, and declarator levels arrive as a prepared list rather than being
13200/// rediscovered inside the walk.
13201fn cpp_comparable_type_shape(
13202    type_node: Node<'_>,
13203    qualifiers: CppCvQualifiers,
13204    levels: Vec<CppComparableLevel>,
13205    source: &str,
13206    lexical_scope: &[String],
13207) -> Option<CppComparableParameter> {
13208    enum Work<'tree> {
13209        Visit {
13210            node: Node<'tree>,
13211            qualifiers: CppCvQualifiers,
13212        },
13213        ApplyLevels(Vec<CppComparableLevel>),
13214        BuildGeneric {
13215            argument_count: usize,
13216        },
13217    }
13218
13219    let mut nodes: Vec<CppComparableNode> = Vec::new();
13220    let mut values: Vec<usize> = Vec::new();
13221    let mut work = vec![
13222        Work::ApplyLevels(levels),
13223        Work::Visit {
13224            node: type_node,
13225            qualifiers,
13226        },
13227    ];
13228    while let Some(next) = work.pop() {
13229        match next {
13230            Work::Visit { node, qualifiers } => match node.kind() {
13231                "type_descriptor" => {
13232                    let inner_type = node
13233                        .child_by_field_name("type")
13234                        .or_else(|| node.named_child(0))?;
13235                    let mut cursor = node.walk();
13236                    let declarator = node.child_by_field_name("declarator").or_else(|| {
13237                        node.named_children(&mut cursor).find(|child| {
13238                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
13239                        })
13240                    });
13241                    let levels = match declarator {
13242                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
13243                        None => Vec::new(),
13244                    };
13245                    work.push(Work::ApplyLevels(levels));
13246                    work.push(Work::Visit {
13247                        node: inner_type,
13248                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
13249                    });
13250                }
13251                "sized_type_specifier" => {
13252                    // `unsigned char` is one primitive type whose components are
13253                    // partly unnamed tokens, so the whole specifier is its own
13254                    // name component. Reducing it to the `type` child would make
13255                    // `f(unsigned char)` and `f(char)` compare equal.
13256                    let name = StructuredTypeName::new(
13257                        vec![normalize_cpp_whitespace(node_text(node, source))],
13258                        lexical_scope.to_vec(),
13259                        false,
13260                    )?;
13261                    values.push(cpp_push_comparable_node(
13262                        &mut nodes,
13263                        CppComparableNode::Named {
13264                            name,
13265                            primitive: true,
13266                            konst: qualifiers.konst,
13267                            volatil: qualifiers.volatil,
13268                        },
13269                    ));
13270                }
13271                "qualified_identifier"
13272                | "scoped_identifier"
13273                | "scoped_type_identifier"
13274                | "type_identifier"
13275                | "field_identifier"
13276                | "identifier"
13277                | "namespace_identifier"
13278                | "primitive_type"
13279                | "template_type" => {
13280                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
13281                    values.push(cpp_push_comparable_node(
13282                        &mut nodes,
13283                        CppComparableNode::Named {
13284                            name,
13285                            primitive: node.kind() == "primitive_type",
13286                            konst: qualifiers.konst,
13287                            volatil: qualifiers.volatil,
13288                        },
13289                    ));
13290                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
13291                        let mut cursor = arguments_node.walk();
13292                        let arguments = arguments_node
13293                            .named_children(&mut cursor)
13294                            .filter(|child| !child.is_extra() && child.kind() != "comment")
13295                            .collect::<Vec<_>>();
13296                        work.push(Work::BuildGeneric {
13297                            argument_count: arguments.len(),
13298                        });
13299                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
13300                            node: argument,
13301                            qualifiers: CppCvQualifiers::default(),
13302                        }));
13303                    }
13304                }
13305                _ => {
13306                    let inner = node.child_by_field_name("type").or_else(|| {
13307                        (node.named_child_count() == 1)
13308                            .then(|| node.named_child(0))
13309                            .flatten()
13310                    })?;
13311                    work.push(Work::Visit {
13312                        node: inner,
13313                        qualifiers,
13314                    });
13315                }
13316            },
13317            Work::ApplyLevels(levels) => {
13318                let mut root = values.pop()?;
13319                for level in levels {
13320                    let node = match level {
13321                        CppComparableLevel::Pointer { konst, volatil } => {
13322                            CppComparableNode::Pointer {
13323                                inner: root,
13324                                konst,
13325                                volatil,
13326                            }
13327                        }
13328                        CppComparableLevel::Reference => {
13329                            CppComparableNode::Reference { inner: root }
13330                        }
13331                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
13332                    };
13333                    root = cpp_push_comparable_node(&mut nodes, node);
13334                }
13335                values.push(root);
13336            }
13337            Work::BuildGeneric { argument_count } => {
13338                let value_count = argument_count.checked_add(1)?;
13339                let start = values.len().checked_sub(value_count)?;
13340                let mut built = values.split_off(start);
13341                let base = built.remove(0);
13342                values.push(cpp_push_comparable_node(
13343                    &mut nodes,
13344                    CppComparableNode::Generic {
13345                        base,
13346                        arguments: built,
13347                    },
13348                ));
13349            }
13350        }
13351    }
13352    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
13353    debug_assert_eq!(
13354        root,
13355        nodes.len().saturating_sub(1),
13356        "comparable nodes are appended in post-order, so the root is the last one"
13357    );
13358    Some(CppComparableParameter { nodes, root })
13359}
13360
13361fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
13362    nodes.push(node);
13363    nodes.len() - 1
13364}
13365
13366/// The template argument list of the name `node` terminates in, if any.
13367///
13368/// `std::vector<int>` writes its arguments on the `name` of a qualified
13369/// identifier, so a walk that stopped at the qualified node would reduce
13370/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
13371fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
13372    let mut current = node;
13373    loop {
13374        match current.kind() {
13375            "template_type" => return current.child_by_field_name("arguments"),
13376            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
13377                current = current.child_by_field_name("name")?;
13378            }
13379            _ => return None,
13380        }
13381    }
13382}
13383
13384/// The callable declarator of the declaration that covers `start_byte`.
13385///
13386/// A consumer that holds a declaration's recorded byte position rather than its
13387/// syntax node - external header extraction, for instance - uses this to reach
13388/// the same `function_declarator` the declaration walk read.
13389pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
13390    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
13391    loop {
13392        if matches!(
13393            current.kind(),
13394            "declaration" | "field_declaration" | "function_definition"
13395        ) && let Some(declarator) = current
13396            .child_by_field_name("declarator")
13397            .and_then(extract_function_declarator)
13398        {
13399            return Some(declarator);
13400        }
13401        current = current.parent()?;
13402    }
13403}
13404
13405fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
13406    let mut labels = Vec::new();
13407    let mut cursor = parameters_node.walk();
13408    for child in parameters_node.children(&mut cursor) {
13409        match child.kind() {
13410            "parameter_declaration" | "optional_parameter_declaration" => {
13411                if let Some(name_node) = child
13412                    .child_by_field_name("declarator")
13413                    .and_then(cpp_declarator_label_node)
13414                {
13415                    labels.push(name_node);
13416                } else {
13417                    labels.push(child);
13418                }
13419            }
13420            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
13421                labels.push(child);
13422            }
13423            _ => {}
13424        }
13425    }
13426    labels
13427}
13428
13429fn cpp_signature_search_start<'tree>(
13430    signature: &str,
13431    function_declarator: Node<'tree>,
13432    source: &str,
13433    ancestry: &ParentIndex<'tree>,
13434) -> usize {
13435    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
13436        return 0;
13437    };
13438    let raw = node_text(enclosing, source);
13439    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
13440    let offset = function_declarator
13441        .start_byte()
13442        .saturating_sub(enclosing.start_byte())
13443        .saturating_sub(leading_trim_bytes);
13444    offset.min(signature.len())
13445}
13446
13447fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
13448    match node.kind() {
13449        "identifier" | "field_identifier" => Some(node),
13450        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
13451            .child_by_field_name("declarator")
13452            .or_else(|| last_named_child(node))
13453            .and_then(cpp_declarator_label_node),
13454        "array_declarator" => node
13455            .child_by_field_name("declarator")
13456            .and_then(cpp_declarator_label_node),
13457        "function_declarator" => node
13458            .child_by_field_name("declarator")
13459            .or_else(|| node.child_by_field_name("name"))
13460            .or_else(|| last_named_child(node))
13461            .and_then(cpp_declarator_label_node),
13462        _ => None,
13463    }
13464}
13465
13466fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
13467    let base_type = parameter
13468        .child_by_field_name("type")
13469        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
13470        .unwrap_or_default();
13471    let declarator = cpp_parameter_declarator(parameter);
13472    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
13473    // are discarded, so `f(const int)` and `f(int)` declare one function. A
13474    // qualifier written next to the parameter's type is only top-level when
13475    // the declarator adds no indirection; behind a pointer, reference or array
13476    // declarator the same qualifier belongs to the pointee, referent or
13477    // element and keeps distinguishing the type (#1827).
13478    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
13479    let mut cursor = parameter.walk();
13480    let qualifiers = parameter
13481        .named_children(&mut cursor)
13482        .filter(|child| child.kind() == "type_qualifier")
13483        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13484        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
13485        .collect::<Vec<_>>()
13486        .join(" ");
13487    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
13488        (true, _) => base_type,
13489        (_, true) => qualifiers,
13490        (false, false) => format!("{qualifiers} {base_type}"),
13491    };
13492    let declarator_suffix = declarator
13493        .map(|node| cpp_declarator_suffix_without_name(node, source))
13494        .unwrap_or_default();
13495
13496    let combined = if type_text.is_empty() {
13497        declarator_suffix
13498    } else if declarator_suffix.is_empty() {
13499        type_text
13500    } else {
13501        format!("{type_text} {declarator_suffix}")
13502    };
13503    normalize_cpp_type_text(&combined)
13504}
13505
13506fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
13507    parameter.child_by_field_name("declarator").or_else(|| {
13508        // Some unnamed prototype parameters expose their abstract declarator
13509        // as a direct named child without the grammar's `declarator` field.
13510        // Recover only the structured abstract-declarator node; the parameter's
13511        // type and qualifiers are distinct children and must not be guessed from
13512        // source text.
13513        let mut cursor = parameter.walk();
13514        parameter
13515            .named_children(&mut cursor)
13516            .find(|child| is_cpp_abstract_declarator(child.kind()))
13517    })
13518}
13519
13520/// Whether a parameter's declarator chain adds indirection - a pointer,
13521/// reference, array or function declarator - to the parameter's written type.
13522pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
13523    let mut current = Some(declarator);
13524    while let Some(node) = current {
13525        if matches!(
13526            node.kind(),
13527            "pointer_declarator"
13528                | "abstract_pointer_declarator"
13529                | "reference_declarator"
13530                | "abstract_reference_declarator"
13531                | "array_declarator"
13532                | "abstract_array_declarator"
13533                | "function_declarator"
13534                | "abstract_function_declarator"
13535        ) {
13536            return true;
13537        }
13538        current = cpp_nested_declarator(node);
13539    }
13540    false
13541}
13542
13543fn is_cpp_abstract_declarator(kind: &str) -> bool {
13544    matches!(
13545        kind,
13546        "abstract_pointer_declarator"
13547            | "abstract_reference_declarator"
13548            | "abstract_array_declarator"
13549            | "abstract_function_declarator"
13550            | "abstract_parenthesized_declarator"
13551    )
13552}
13553
13554fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
13555    node.child_by_field_name("declarator").or_else(|| {
13556        if is_cpp_abstract_declarator(node.kind()) {
13557            let mut cursor = node.walk();
13558            node.named_children(&mut cursor)
13559                .find(|child| is_cpp_abstract_declarator(child.kind()))
13560        } else {
13561            // Named declarators historically use their last named child when
13562            // tree-sitter omits the field. Keep that broad fallback for
13563            // attributed, variadic, and recovered named shapes.
13564            last_named_child(node)
13565        }
13566    })
13567}
13568
13569fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
13570    match node.kind() {
13571        "identifier" | "field_identifier" => String::new(),
13572        "pointer_declarator" | "abstract_pointer_declarator" => {
13573            let inner = cpp_nested_declarator(node)
13574                .map(|child| cpp_declarator_suffix_without_name(child, source))
13575                .unwrap_or_default();
13576            format!("*{inner}")
13577        }
13578        "reference_declarator" | "abstract_reference_declarator" => {
13579            let inner = cpp_nested_declarator(node)
13580                .map(|child| cpp_declarator_suffix_without_name(child, source))
13581                .unwrap_or_default();
13582            let reference = node
13583                .children(&mut node.walk())
13584                .find(|child| matches!(child.kind(), "&" | "&&"))
13585                .map(|child| node_text(child, source))
13586                .unwrap_or("&");
13587            format!("{reference}{inner}")
13588        }
13589        "array_declarator" | "abstract_array_declarator" => {
13590            let inner = cpp_nested_declarator(node)
13591                .map(|child| cpp_declarator_suffix_without_name(child, source))
13592                .unwrap_or_default();
13593            let size = node
13594                .child_by_field_name("size")
13595                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
13596                .unwrap_or_default();
13597            format!("{inner}[{size}]")
13598        }
13599        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
13600            let inner = cpp_nested_declarator(node);
13601            inner
13602                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
13603                .unwrap_or_default()
13604        }
13605        "function_declarator" | "abstract_function_declarator" => {
13606            let inner = cpp_nested_declarator(node)
13607                .map(|child| cpp_declarator_suffix_without_name(child, source))
13608                .unwrap_or_default();
13609            let params = node
13610                .child_by_field_name("parameters")
13611                .map(|child| cpp_parameter_signature(child, source))
13612                .unwrap_or_else(|| "()".to_string());
13613            format!("{inner}{params}")
13614        }
13615        _ => {
13616            let text = normalize_cpp_whitespace(node_text(node, source));
13617            let name = extract_declarator_name(node, source);
13618            if name.is_empty() {
13619                text
13620            } else {
13621                text.replace(&name, "").trim().to_string()
13622            }
13623        }
13624    }
13625}
13626
13627fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
13628    collapse_cpp_whitespace(
13629        suffix
13630            .trim()
13631            .trim_start_matches("->")
13632            .trim_start_matches('{')
13633            .trim_end_matches(';'),
13634    )
13635}
13636
13637pub fn normalize_cpp_whitespace(value: &str) -> String {
13638    collapse_cpp_whitespace(value)
13639}
13640
13641fn normalize_cpp_type_text(value: &str) -> String {
13642    collapse_cpp_whitespace(value)
13643        .replace(", ", ",")
13644        .replace(" <", "<")
13645        .replace("< ", "<")
13646        .replace(" >", ">")
13647}
13648
13649fn collapse_cpp_whitespace(value: &str) -> String {
13650    let mut result = String::new();
13651    let mut prev_space = false;
13652    for ch in value.chars() {
13653        if ch.is_whitespace() {
13654            if !prev_space {
13655                result.push(' ');
13656            }
13657            prev_space = true;
13658        } else {
13659            result.push(ch);
13660            prev_space = false;
13661        }
13662    }
13663    result.trim().to_string()
13664}
13665
13666pub fn node_text<'a>(node: Node<'_>, source: &'a str) -> &'a str {
13667    node_source_text(node, source)
13668}
13669
13670pub fn collect_cpp_identifiers(node: Node<'_>, source: &str, identifiers: &mut HashSet<String>) {
13671    walk_named_tree_preorder(node, true, |node| {
13672        match node.kind() {
13673            "type_identifier" | "identifier" | "qualified_identifier" => {
13674                let text = node_text(node, source).trim();
13675                if !text.is_empty() {
13676                    identifiers.insert(text.to_string());
13677                }
13678            }
13679            _ => {}
13680        }
13681        WalkControl::Continue
13682    });
13683}
13684
13685fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
13686    node.child_by_field_name("body").or_else(|| {
13687        let mut cursor = node.walk();
13688        node.named_children(&mut cursor).find(|child| {
13689            matches!(
13690                child.kind(),
13691                "declaration_list" | "field_declaration_list" | "enumerator_list"
13692            )
13693        })
13694    })
13695}
13696
13697/// Return a class body's actual closing brace when the parser supplied one.
13698///
13699/// A malformed namespace sentinel can leave a class node carrying unrelated
13700/// parser errors even though its own class body is complete.  `has_error()` is
13701/// therefore too coarse an admission predicate for sentinel ownership.  The
13702/// body list, however, exposes the opening and closing punctuation directly;
13703/// a real (non-missing) final `}` proves that the class did not borrow the
13704/// enclosing namespace's close.  Requiring the body to end before its parent
13705/// container also rejects a recovered node whose body swallowed that outer
13706/// boundary.
13707fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
13708    if !matches!(
13709        node.kind(),
13710        "class_specifier" | "struct_specifier" | "union_specifier"
13711    ) {
13712        return None;
13713    }
13714    let body = cpp_body_node(node)?;
13715    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
13716        return None;
13717    }
13718    let open = body.child(0)?;
13719    let close = body.child(body.child_count().checked_sub(1)?)?;
13720    if open.kind() != "{"
13721        || open.is_missing()
13722        || close.kind() != "}"
13723        || close.is_missing()
13724        || close.end_byte() != body.end_byte()
13725        || body.end_byte() > node.end_byte()
13726        || node
13727            .parent()
13728            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
13729    {
13730        return None;
13731    }
13732    Some(close)
13733}
13734
13735fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
13736    if node.kind() == "namespace_definition" {
13737        return true;
13738    }
13739    let mut cursor = node.walk();
13740    node.named_children(&mut cursor)
13741        .any(cpp_contains_namespace_definition)
13742}
13743
13744struct CppNestedNamespaceSentinel<'tree> {
13745    function: Node<'tree>,
13746    body: Node<'tree>,
13747    namespace_components: Vec<String>,
13748}
13749
13750/// Owned structural recovery metadata for a namespace-sentinel region.
13751///
13752/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
13753/// instead of the namespace/class scopes that the declaration visitor restores.
13754/// The inverted usage walk has the original CST, so it needs the same ownership
13755/// evidence without borrowing parser nodes across its file scan.  Keep this
13756/// descriptor deliberately source-range based: callers can match a reference
13757/// node by containment and then resolve its structured type spelling in the
13758/// recovered class scope.
13759#[derive(Debug, Clone)]
13760pub struct CppSentinelRecoveredOwner {
13761    pub range: Range,
13762    /// Start of the qualified owner name (`btree<P>::method`).  A leading
13763    /// return type before this byte is looked up from the namespace; parameters,
13764    /// trailing returns, and the body use the member owner scope.
13765    pub owner_name_start_byte: usize,
13766    /// Number of leading components belonging to the namespace rather than
13767    /// the qualified class owner.  A leading return type is looked up before
13768    /// every owner component, not merely before the innermost class.
13769    pub namespace_component_count: usize,
13770    pub scope_components: Vec<String>,
13771}
13772
13773#[derive(Debug, Clone)]
13774pub struct CppSentinelRecoveredClass {
13775    pub namespace_range: Range,
13776    pub namespace_scope_components: Vec<String>,
13777    pub class_range: Range,
13778    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
13779    pub scope_components: Vec<String>,
13780    /// Qualified out-of-line member definitions owned by this class.  Their
13781    /// ranges may extend beyond `class_range` when the malformed sentinel
13782    /// swallowed the namespace close and left definitions as function siblings.
13783    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
13784}
13785
13786/// Resolve the lexical scope restored for a node in a malformed
13787/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
13788/// outrank class spans, which in turn outrank the surviving namespace body.
13789/// The class ancestor suffix is recovered from the original CST so nested
13790/// members keep their complete `Outer::Inner` owner chain.
13791pub fn cpp_sentinel_recovered_scope_for_node(
13792    node: Node<'_>,
13793    source: &str,
13794    recovered_classes: &[CppSentinelRecoveredClass],
13795) -> Option<Vec<String>> {
13796    let contains =
13797        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
13798    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
13799    for recovered in recovered_classes {
13800        for owner in recovered
13801            .owner_ranges
13802            .iter()
13803            .filter(|owner| contains(owner.range))
13804        {
13805            let replace = best_owner.is_none_or(|existing| {
13806                owner.range.end_byte.saturating_sub(owner.range.start_byte)
13807                    < existing
13808                        .range
13809                        .end_byte
13810                        .saturating_sub(existing.range.start_byte)
13811            });
13812            if replace {
13813                best_owner = Some(owner);
13814            }
13815        }
13816    }
13817    if let Some(owner) = best_owner {
13818        let mut scope = owner.scope_components.clone();
13819        if node.start_byte() < owner.owner_name_start_byte {
13820            scope.truncate(owner.namespace_component_count);
13821        }
13822        return Some(scope);
13823    }
13824
13825    let class = recovered_classes
13826        .iter()
13827        .filter(|recovered| contains(recovered.class_range))
13828        .min_by_key(|recovered| {
13829            recovered
13830                .class_range
13831                .end_byte
13832                .saturating_sub(recovered.class_range.start_byte)
13833        });
13834    let class_scope = class.is_some();
13835    let mut scope = if let Some(class) = class {
13836        class.scope_components.clone()
13837    } else {
13838        let namespace = recovered_classes
13839            .iter()
13840            .filter(|recovered| contains(recovered.namespace_range))
13841            .min_by_key(|recovered| {
13842                recovered
13843                    .namespace_range
13844                    .end_byte
13845                    .saturating_sub(recovered.namespace_range.start_byte)
13846            })?;
13847        let mut scope = namespace.namespace_scope_components.clone();
13848        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
13849        let common_prefix = scope
13850            .iter()
13851            .zip(&parser_namespace)
13852            .take_while(|(recovered, parser)| recovered == parser)
13853            .count();
13854        scope.extend(parser_namespace.into_iter().skip(common_prefix));
13855        scope
13856    };
13857    if class_scope {
13858        let mut ancestor_components = Vec::new();
13859        let mut ancestor = node.parent();
13860        while let Some(current) = ancestor {
13861            if matches!(
13862                current.kind(),
13863                "class_specifier" | "struct_specifier" | "union_specifier"
13864            ) && let Some(name) = current.child_by_field_name("name")
13865                && let Some(name_components) = cpp_name_components(name, source)
13866            {
13867                ancestor_components.push(
13868                    name_components
13869                        .into_iter()
13870                        .map(|component| component.name)
13871                        .collect::<Vec<_>>(),
13872                );
13873            }
13874            ancestor = current.parent();
13875        }
13876        ancestor_components.reverse();
13877        let base_len = scope.len();
13878        for component in ancestor_components.into_iter().flatten() {
13879            if scope.len() >= base_len && scope.last() == Some(&component) {
13880                continue;
13881            }
13882            scope.push(component);
13883        }
13884    }
13885    Some(scope)
13886}
13887
13888struct CppSentinelFragmentedClassTail<'tree> {
13889    class_node: Node<'tree>,
13890    template_node: Option<Node<'tree>>,
13891    name: String,
13892    raw_supertypes: Option<Vec<String>>,
13893    fragmented: FragmentedExportBody,
13894    consumed_start: usize,
13895}
13896
13897struct CppSentinelFragmentedClassErrorPrefix<'tree> {
13898    name: String,
13899    open: Node<'tree>,
13900    raw_supertypes: Option<Vec<String>>,
13901}
13902
13903struct CppSentinelDirectBodyClassRegion {
13904    namespace_components: Vec<String>,
13905    class_start: usize,
13906    class_start_line: usize,
13907    class_close_end: usize,
13908    class_close_line: usize,
13909    name: String,
13910}
13911
13912fn cpp_sentinel_body_class_candidate<'tree>(
13913    child: Node<'tree>,
13914) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13915    if matches!(
13916        child.kind(),
13917        "class_specifier" | "struct_specifier" | "union_specifier"
13918    ) {
13919        return Some((child, None));
13920    }
13921    if child.kind() != "template_declaration" {
13922        if child.kind() == "declaration" {
13923            return Some((first_class_like_child(child)?, None));
13924        }
13925        return None;
13926    }
13927    let mut cursor = child.walk();
13928    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
13929        if matches!(
13930            candidate.kind(),
13931            "class_specifier" | "struct_specifier" | "union_specifier"
13932        ) {
13933            Some(candidate)
13934        } else if candidate.kind() == "declaration" {
13935            first_class_like_child(candidate)
13936        } else {
13937            None
13938        }
13939    })?;
13940    Some((class_node, Some(child)))
13941}
13942
13943/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
13944/// namespace-sentinel body when a later member macro ends the bogus sentinel
13945/// function before the real class close. The anonymous class/open tokens and
13946/// direct identifier are the structural proof; a retained direct close would
13947/// be an ordinary malformed class rather than the fragmented tail handled here.
13948fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
13949    node: Node<'tree>,
13950    source: &str,
13951) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
13952    let name = malformed_class_error_owner_name(node, source)?;
13953    let mut cursor = node.walk();
13954    let children = node.children(&mut cursor).collect::<Vec<_>>();
13955    let keyword = children.first()?;
13956    let open_index = children.iter().position(|child| child.kind() == "{")?;
13957    if children[open_index + 1..]
13958        .iter()
13959        .any(|child| child.kind() == "}")
13960    {
13961        return None;
13962    }
13963    let raw_supertypes =
13964        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
13965    Some(CppSentinelFragmentedClassErrorPrefix {
13966        name,
13967        open: children[open_index],
13968        raw_supertypes,
13969    })
13970}
13971
13972fn cpp_sentinel_direct_body_class_candidate<'tree>(
13973    child: Node<'tree>,
13974) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
13975    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
13976        return Some(candidate);
13977    }
13978    if child.kind() != "template_declaration" {
13979        return None;
13980    }
13981    let mut cursor = child.walk();
13982    let wrapper = child
13983        .named_children(&mut cursor)
13984        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
13985    Some((first_class_like_child(wrapper)?, Some(child)))
13986}
13987
13988fn cpp_sentinel_direct_namespace_components(
13989    function: Node<'_>,
13990    body: Node<'_>,
13991    source: &str,
13992) -> Option<Vec<String>> {
13993    let mut cursor = function.walk();
13994    let children = function
13995        .named_children(&mut cursor)
13996        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
13997        .collect::<Vec<_>>();
13998    let sentinel_index = children.iter().rposition(|child| {
13999        direct_identifier_name(*child, source)
14000            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
14001    })?;
14002    let mut identifiers = Vec::new();
14003    let mut stack = children[sentinel_index + 1..]
14004        .iter()
14005        .rev()
14006        .copied()
14007        .collect::<Vec<_>>();
14008    while let Some(current) = stack.pop() {
14009        if let Some(name) = direct_identifier_name(current, source) {
14010            identifiers.push(name);
14011            continue;
14012        }
14013        let mut cursor = current.walk();
14014        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
14015        stack.extend(children.into_iter().rev());
14016    }
14017    let [keyword, namespace] = identifiers.as_slice() else {
14018        return None;
14019    };
14020    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
14021        .then(|| vec![namespace.clone()])
14022}
14023
14024fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
14025    let mut sibling = class_semicolon.next_named_sibling();
14026    let namespace_close = loop {
14027        let Some(current) = sibling else {
14028            return false;
14029        };
14030        sibling = current.next_named_sibling();
14031        if current.kind() != "comment" {
14032            break current;
14033        }
14034    };
14035    if !cpp_is_stray_close_brace(namespace_close, source) {
14036        return false;
14037    }
14038    loop {
14039        let Some(current) = sibling else {
14040            return false;
14041        };
14042        sibling = current.next_named_sibling();
14043        if current.kind() == "comment" {
14044            continue;
14045        }
14046        return direct_identifier_name(current, source)
14047            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
14048    }
14049}
14050
14051fn cpp_sentinel_macro_body_class_region<'tree>(
14052    node: Node<'tree>,
14053    source: &str,
14054    ancestry: &ParentIndex<'tree>,
14055) -> Option<CppSentinelDirectBodyClassRegion> {
14056    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
14057        return None;
14058    };
14059    if node.kind() != "function_definition" || !node.has_error() {
14060        return None;
14061    }
14062    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
14063    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
14064    let mut cursor = body.walk();
14065    let candidates = body
14066        .named_children(&mut cursor)
14067        .filter_map(cpp_sentinel_direct_body_class_candidate)
14068        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
14069        .collect::<Vec<_>>();
14070    let [(class_node, template_node)] = candidates.as_slice() else {
14071        return None;
14072    };
14073    let original_body = cpp_body_node(*class_node)?;
14074    let name = class_like_name(*class_node, source, ancestry)?;
14075    if name.is_empty() || cpp_export_macro_token(&name) {
14076        return None;
14077    }
14078
14079    let mut sibling = node.next_named_sibling();
14080    let (class_close_start, class_close_end, class_close_line) = loop {
14081        let current = sibling?;
14082        let next = current.next_named_sibling();
14083        if cpp_is_stray_close_brace(current, source)
14084            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
14085        {
14086            let semicolon = next.expect("checked above");
14087            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
14088                return None;
14089            }
14090            break (
14091                current.start_byte(),
14092                semicolon.end_byte(),
14093                semicolon.end_position().row + 1,
14094            );
14095        }
14096        sibling = next;
14097    };
14098    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
14099    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
14100    let root = tree.root_node();
14101    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
14102    // The region reparse is its own tree, so it needs its own parent index;
14103    // the caller's index answers nothing about these nodes.
14104    let reparsed_ancestry = ParentIndex::new(root);
14105    let reparsed =
14106        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
14107    if reparsed.name != name
14108        || reparsed.declaration_node.start_byte() != class_node.start_byte()
14109        || reparsed.body.start_byte() != original_body.start_byte()
14110        || class_close_start <= reparsed.body.end_byte()
14111        || class_close_end <= class_node.end_byte()
14112    {
14113        return None;
14114    }
14115    Some(CppSentinelDirectBodyClassRegion {
14116        namespace_components,
14117        class_start: reparse_start,
14118        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
14119            node.start_position().row + 1
14120        }),
14121        class_close_end,
14122        class_close_line,
14123        name,
14124    })
14125}
14126
14127/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
14128/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
14129///
14130/// The parser puts the namespace opener and the malformed function in one root
14131/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
14132/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
14133/// the malformed function must begin with an all-caps type, then an ERROR whose
14134/// sole identifier is `namespace`, followed by the inner namespace identifier
14135/// and a compound body; and that body must contain a complete named class or a
14136/// structurally fragmented class prefix. A text reparse cannot prove any of
14137/// those ownership boundaries.
14138fn cpp_nested_namespace_sentinel<'tree>(
14139    node: Node<'tree>,
14140    source: &str,
14141    ancestry: &ParentIndex<'tree>,
14142) -> Option<CppNestedNamespaceSentinel<'tree>> {
14143    if !node.has_error() {
14144        return None;
14145    }
14146
14147    let (function, mut namespace_components) = if node.kind() == "ERROR" {
14148        let mut cursor = node.walk();
14149        let functions = node
14150            .named_children(&mut cursor)
14151            .filter(|child| child.kind() == "function_definition")
14152            .collect::<Vec<_>>();
14153        let [function] = functions.as_slice() else {
14154            return None;
14155        };
14156        if !function.has_error() {
14157            return None;
14158        }
14159        let mut cursor = node.walk();
14160        let children = node.children(&mut cursor).collect::<Vec<_>>();
14161        let function_index = children
14162            .iter()
14163            .position(|child| same_node(*child, *function))?;
14164        let [outer_keyword, outer_name, outer_open] =
14165            children.get(function_index.checked_sub(3)?..function_index)?
14166        else {
14167            return None;
14168        };
14169        if outer_keyword.kind() != "namespace"
14170            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
14171            || outer_open.kind() != "{"
14172        {
14173            return None;
14174        }
14175        (
14176            *function,
14177            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
14178        )
14179    } else if node.kind() == "function_definition" {
14180        let declaration_list = node.parent()?;
14181        let namespace = declaration_list.parent()?;
14182        if declaration_list.kind() != "declaration_list"
14183            || namespace.kind() != "namespace_definition"
14184            || namespace.child_by_field_name("body") != Some(declaration_list)
14185        {
14186            return None;
14187        }
14188        (node, Vec::new())
14189    } else {
14190        return None;
14191    };
14192
14193    let mut cursor = function.walk();
14194    let named = function
14195        .named_children(&mut cursor)
14196        .filter(|child| child.kind() != "comment")
14197        .collect::<Vec<_>>();
14198    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
14199        return None;
14200    };
14201    if first_type.kind() != "type_identifier" {
14202        return None;
14203    }
14204    let sentinel = normalize_cpp_whitespace(node_text(*first_type, source));
14205    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
14206        return None;
14207    }
14208    if inner_error.kind() != "ERROR" || inner_error.named_child_count() != 1 {
14209        return None;
14210    }
14211    let inner_keyword = inner_error.named_child(0)?;
14212    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
14213        return None;
14214    }
14215    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
14216        return None;
14217    }
14218    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
14219    if inner_name.is_empty() || body.kind() != "compound_statement" {
14220        return None;
14221    }
14222    namespace_components.push(inner_name);
14223
14224    let mut cursor = body.walk();
14225    let has_complete_class = body.named_children(&mut cursor).any(|child| {
14226        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
14227            cpp_body_node(class_node).is_some()
14228                && class_like_name(class_node, source, ancestry)
14229                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
14230        })
14231    });
14232    if !has_complete_class
14233        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
14234    {
14235        return None;
14236    }
14237
14238    Some(CppNestedNamespaceSentinel {
14239        function,
14240        body: *body,
14241        namespace_components,
14242    })
14243}
14244
14245/// Recognize a namespace-begin sentinel directly beneath the translation unit.
14246///
14247/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
14248/// function whose type is the sentinel, whose declarator is the structured
14249/// qualified name `namespace::a::b`, and whose body contains the namespace
14250/// items. Declaration indexing already reparses this bounded region. The
14251/// inverse scanner retains the original tree, so recover the same namespace
14252/// components from the declarator fields for its lexical-scope metadata.
14253fn cpp_root_namespace_sentinel<'tree>(
14254    node: Node<'tree>,
14255    source: &str,
14256    ancestry: &ParentIndex<'tree>,
14257) -> Option<CppNestedNamespaceSentinel<'tree>> {
14258    if node.kind() != "function_definition"
14259        || !node.has_error()
14260        || node.parent()?.kind() != "translation_unit"
14261    {
14262        return None;
14263    }
14264    let first_type = node.child_by_field_name("type")?;
14265    let sentinel = normalize_cpp_whitespace(node_text(first_type, source));
14266    if first_type.kind() != "type_identifier"
14267        || sentinel.is_empty()
14268        || !cpp_export_macro_token(&sentinel)
14269    {
14270        return None;
14271    }
14272    let declarator = node.child_by_field_name("declarator")?;
14273    let body = node.child_by_field_name("body")?;
14274    if declarator.kind() != "qualified_identifier" || body.kind() != "compound_statement" {
14275        return None;
14276    }
14277    let mut cursor = node.walk();
14278    let named = node
14279        .named_children(&mut cursor)
14280        .filter(|child| child.kind() != "comment")
14281        .collect::<Vec<_>>();
14282    let [named_type, named_declarator, named_body] = named.as_slice() else {
14283        return None;
14284    };
14285    if !same_node(*named_type, first_type)
14286        || !same_node(*named_declarator, declarator)
14287        || !same_node(*named_body, body)
14288    {
14289        return None;
14290    }
14291    let mut declarator_components = Vec::new();
14292    let mut valid_components = true;
14293    walk_named_tree_preorder(declarator, true, |component| {
14294        if !matches!(
14295            component.kind(),
14296            "identifier" | "namespace_identifier" | "type_identifier"
14297        ) {
14298            return WalkControl::Continue;
14299        }
14300        let Some(component) = canonical_cpp_qualified_component(component, source) else {
14301            valid_components = false;
14302            return WalkControl::Break;
14303        };
14304        declarator_components.push(component.name);
14305        WalkControl::SkipChildren
14306    });
14307    if !valid_components || declarator_components.first().map(String::as_str) != Some("namespace") {
14308        return None;
14309    }
14310    declarator_components.remove(0);
14311    let namespace_components = declarator_components;
14312    if namespace_components.is_empty()
14313        || namespace_components
14314            .iter()
14315            .any(|component| component.is_empty() || cpp_export_macro_token(component))
14316    {
14317        return None;
14318    }
14319
14320    let mut cursor = body.walk();
14321    let has_complete_class = body.named_children(&mut cursor).any(|child| {
14322        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
14323            cpp_body_node(class_node).is_some()
14324                && class_like_name(class_node, source, ancestry)
14325                    .is_some_and(|name| !name.is_empty() && !cpp_export_macro_token(&name))
14326        })
14327    });
14328    if !has_complete_class
14329        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
14330    {
14331        return None;
14332    }
14333
14334    Some(CppNestedNamespaceSentinel {
14335        function: node,
14336        body,
14337        namespace_components,
14338    })
14339}
14340
14341/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
14342/// malformed namespace-sentinel function.  The recovery is deliberately
14343/// structural: the class must be a direct body item, its own class node must be
14344/// erroneous and end before a unique anonymous `}` in the enclosing
14345/// declaration-list, and that namespace's next sibling must be a standalone
14346/// `;`.  The complete interior must pass the existing member-shaped reparse
14347/// gate. This avoids source brace scans and does not borrow a close from an
14348/// unrelated later declaration.
14349fn cpp_sentinel_fragmented_class_tail<'tree>(
14350    function: Node<'tree>,
14351    body: Node<'tree>,
14352    source: &str,
14353    ancestry: &ParentIndex<'tree>,
14354) -> Option<CppSentinelFragmentedClassTail<'tree>> {
14355    let mut cursor = body.walk();
14356    let candidates = body
14357        .named_children(&mut cursor)
14358        .filter_map(|child| {
14359            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
14360                let class_body = cpp_body_node(class_node)?;
14361                if !class_node.has_error() {
14362                    return None;
14363                }
14364                let name = class_like_name(class_node, source, ancestry)?;
14365                let raw_supertypes =
14366                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
14367                        .then(|| extract_cpp_supertypes(class_node, source));
14368                return Some((
14369                    class_node,
14370                    template_node,
14371                    name,
14372                    class_body,
14373                    class_body.start_byte().checked_add(1)?,
14374                    raw_supertypes,
14375                ));
14376            }
14377            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
14378            Some((
14379                child,
14380                None,
14381                prefix.name,
14382                prefix.open,
14383                prefix.open.end_byte(),
14384                prefix.raw_supertypes,
14385            ))
14386        })
14387        .collect::<Vec<_>>();
14388    let [(class_node, template_node, name, class_body, reparse_start, raw_supertypes)] =
14389        candidates.as_slice()
14390    else {
14391        return None;
14392    };
14393    if name.is_empty() || cpp_export_macro_token(name) {
14394        return None;
14395    }
14396
14397    let (close, semicolon) =
14398        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
14399
14400    let reparse_end = close.start_byte();
14401    if *reparse_start >= reparse_end {
14402        return None;
14403    }
14404    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
14405    if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
14406        return None;
14407    }
14408    let class_range = Range {
14409        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
14410        end_byte: semicolon.end_byte(),
14411        start_line: template_node.map_or(class_node.start_position().row, |node| {
14412            node.start_position().row
14413        }) + 1,
14414        end_line: semicolon.end_position().row + 1,
14415    };
14416    Some(CppSentinelFragmentedClassTail {
14417        class_node: *class_node,
14418        template_node: *template_node,
14419        name: name.clone(),
14420        raw_supertypes: raw_supertypes.clone(),
14421        fragmented: FragmentedExportBody {
14422            reparse_start: *reparse_start,
14423            reparse_end,
14424            class_range,
14425        },
14426        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
14427    })
14428}
14429
14430/// Recover the class and out-of-line owner scopes from every malformed
14431/// namespace-sentinel region in `root`.
14432///
14433/// This is the shared structural counterpart to
14434/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
14435/// reuses the visitor's sentinel/class admission predicates instead of parsing
14436/// source text a second time.  The returned values own only ranges and names, so
14437/// they can be retained by an inverted usage scan after the tree borrow ends.
14438pub fn cpp_sentinel_recovered_classes(
14439    root: Node<'_>,
14440    source: &str,
14441) -> Vec<CppSentinelRecoveredClass> {
14442    if !root.has_error() {
14443        return Vec::new();
14444    }
14445    // This scan owns its walk of `root`, so it owns the parent index that walk
14446    // asks its ancestor questions through. Built after the error gate: a clean
14447    // tree returns without paying for one.
14448    let ancestry = ParentIndex::new(root);
14449    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
14450    let mut stack = vec![root];
14451    while let Some(current) = stack.pop() {
14452        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
14453            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
14454        {
14455            let namespace_components = cpp_sentinel_recovered_namespace_components(
14456                recovered.function,
14457                &recovered.namespace_components,
14458                source,
14459            );
14460            let fragmented = cpp_sentinel_fragmented_class_tail(
14461                recovered.function,
14462                recovered.body,
14463                source,
14464                &ancestry,
14465            );
14466            let mut class_candidates = Vec::new();
14467            let mut cursor = recovered.body.walk();
14468            for (class_node, template_node) in recovered
14469                .body
14470                .named_children(&mut cursor)
14471                .filter_map(cpp_sentinel_body_class_candidate)
14472            {
14473                let Some(name) = class_like_name(class_node, source, &ancestry) else {
14474                    continue;
14475                };
14476                if name.is_empty() || cpp_export_macro_token(&name) {
14477                    continue;
14478                }
14479                let is_fragmented = fragmented
14480                    .as_ref()
14481                    .is_some_and(|tail| same_node(tail.class_node, class_node));
14482                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
14483                    continue;
14484                }
14485                let class_range = if is_fragmented {
14486                    fragmented
14487                        .as_ref()
14488                        .map(|tail| tail.fragmented.class_range)
14489                        .expect("fragmented class range is present when class matches")
14490                } else {
14491                    cpp_declaration_range(template_node.unwrap_or(class_node))
14492                };
14493                class_candidates.push((class_range, name));
14494            }
14495            if let Some(fragmented) = fragmented
14496                .as_ref()
14497                .filter(|tail| tail.class_node.kind() == "ERROR")
14498            {
14499                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
14500            }
14501
14502            let mut owner_ranges =
14503                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
14504            cpp_sentinel_extend_unique_owner_ranges(
14505                &mut owner_ranges,
14506                cpp_sentinel_recovered_sibling_owner_ranges(
14507                    recovered.function,
14508                    &namespace_components,
14509                    source,
14510                ),
14511            );
14512            for (class_range, name) in class_candidates {
14513                push_cpp_sentinel_recovered_class(
14514                    &mut recovered_classes,
14515                    cpp_declaration_range(recovered.body),
14516                    &namespace_components,
14517                    class_range,
14518                    name,
14519                    &owner_ranges,
14520                );
14521            }
14522
14523            if let Some(declaration_list) = recovered
14524                .function
14525                .parent()
14526                .filter(|parent| parent.kind() == "declaration_list")
14527            {
14528                let outer_namespace =
14529                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
14530                push_cpp_sentinel_sibling_classes(
14531                    &mut recovered_classes,
14532                    declaration_list,
14533                    recovered.function,
14534                    &outer_namespace,
14535                    source,
14536                    &ancestry,
14537                );
14538            }
14539        } else if let Some(region) =
14540            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
14541        {
14542            let namespace_components = cpp_sentinel_recovered_namespace_components(
14543                current,
14544                &region.namespace_components,
14545                source,
14546            );
14547            let owner_container = current
14548                .parent()
14549                .filter(|parent| parent.kind() == "declaration_list")
14550                .unwrap_or(current);
14551            let owner_ranges =
14552                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14553            push_cpp_sentinel_recovered_class(
14554                &mut recovered_classes,
14555                cpp_declaration_range(owner_container),
14556                &namespace_components,
14557                Range {
14558                    start_byte: region.class_start,
14559                    end_byte: region.class_close_end,
14560                    start_line: region.class_start_line,
14561                    end_line: region.class_close_line,
14562                },
14563                region.name,
14564                &owner_ranges,
14565            );
14566        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
14567            // A generic sentinel-prefixed class can be reduced as a malformed
14568            // function/ERROR without the explicit `namespace X` token pair.
14569            // Reuse the declaration visitor's bounded reparse and retain only
14570            // the recovered class identity/range here.
14571            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
14572                region;
14573            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
14574                continue;
14575            };
14576            let root = tree.root_node();
14577            let template_node = cpp_sentinel_reparsed_leading_template(root);
14578            // A region reparse is its own tree and needs its own parent index.
14579            let reparsed_ancestry = ParentIndex::new(root);
14580            let Some(reparsed_class) =
14581                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
14582            else {
14583                continue;
14584            };
14585            let class_node = reparsed_class.declaration_node;
14586            let name = reparsed_class.name;
14587            let namespace_components =
14588                cpp_sentinel_recovered_namespace_components(current, &[], source);
14589            let owner_container = current
14590                .parent()
14591                .filter(|parent| parent.kind() == "declaration_list")
14592                .unwrap_or(current);
14593            let mut owner_ranges =
14594                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
14595            cpp_sentinel_extend_unique_owner_ranges(
14596                &mut owner_ranges,
14597                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
14598            );
14599            push_cpp_sentinel_recovered_class(
14600                &mut recovered_classes,
14601                cpp_declaration_range(owner_container),
14602                &namespace_components,
14603                Range {
14604                    start_byte: class_start,
14605                    end_byte: close_end,
14606                    start_line: class_node.start_position().row + 1,
14607                    end_line: class_node.end_position().row + 1,
14608                },
14609                name,
14610                &owner_ranges,
14611            );
14612            if owner_container.kind() == "declaration_list" {
14613                push_cpp_sentinel_sibling_classes(
14614                    &mut recovered_classes,
14615                    owner_container,
14616                    current,
14617                    &namespace_components,
14618                    source,
14619                    &ancestry,
14620                );
14621            }
14622        }
14623
14624        let mut cursor = current.walk();
14625        stack.extend(current.named_children(&mut cursor));
14626    }
14627    // A shallower sentinel can expose nested classes as apparent namespace
14628    // siblings even after a deeper sentinel proves that a containing class
14629    // owns their ranges. Drop those shadow descriptors; scope recovery starts
14630    // from the proven containing class and appends parser-visible class
14631    // ancestors, preserving the full `Outer::Inner` chain.
14632    let shadowed = recovered_classes
14633        .iter()
14634        .map(|candidate| {
14635            recovered_classes.iter().any(|container| {
14636                container.class_range.start_byte <= candidate.class_range.start_byte
14637                    && container.class_range.end_byte >= candidate.class_range.end_byte
14638                    && container.class_range != candidate.class_range
14639                    && container.namespace_scope_components.len()
14640                        > candidate.namespace_scope_components.len()
14641                    && container
14642                        .namespace_scope_components
14643                        .starts_with(&candidate.namespace_scope_components)
14644            })
14645        })
14646        .collect::<Vec<_>>();
14647    let mut index = 0usize;
14648    recovered_classes.retain(|_| {
14649        let keep = !shadowed[index];
14650        index += 1;
14651        keep
14652    });
14653    recovered_classes
14654}
14655
14656/// A flat sentinel can swallow the first class while leaving later classes and
14657/// their out-of-line definitions as ordinary declaration-list siblings.  Once
14658/// the malformed class proves the sentinel envelope, retain those structurally
14659/// complete sibling classes under the same surviving namespace so every member
14660/// owner in the region uses one recovery contract.
14661fn push_cpp_sentinel_sibling_classes<'tree>(
14662    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14663    declaration_list: Node<'tree>,
14664    sentinel_node: Node<'tree>,
14665    namespace_components: &[String],
14666    source: &str,
14667    ancestry: &ParentIndex<'tree>,
14668) {
14669    let owner_ranges =
14670        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
14671    let namespace_range = cpp_declaration_range(declaration_list);
14672    let mut cursor = declaration_list.walk();
14673    for (class_node, template_node) in declaration_list
14674        .named_children(&mut cursor)
14675        .filter(|child| !same_node(*child, sentinel_node))
14676        .filter_map(cpp_sentinel_body_class_candidate)
14677    {
14678        let Some(name) = class_like_name(class_node, source, ancestry) else {
14679            continue;
14680        };
14681        if name.is_empty()
14682            || cpp_export_macro_token(&name)
14683            || cpp_complete_class_body_close(class_node).is_none()
14684        {
14685            continue;
14686        }
14687        push_cpp_sentinel_recovered_class(
14688            recovered_classes,
14689            namespace_range,
14690            namespace_components,
14691            cpp_declaration_range(template_node.unwrap_or(class_node)),
14692            name,
14693            &owner_ranges,
14694        );
14695    }
14696}
14697
14698fn push_cpp_sentinel_recovered_class(
14699    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
14700    namespace_range: Range,
14701    namespace_components: &[String],
14702    class_range: Range,
14703    name: String,
14704    owner_ranges: &[CppSentinelRecoveredOwner],
14705) {
14706    let mut scope_components = namespace_components.to_vec();
14707    scope_components.push(name);
14708    let owner_ranges = owner_ranges
14709        .iter()
14710        .filter(|owner| owner.scope_components.starts_with(&scope_components))
14711        .cloned()
14712        .collect::<Vec<_>>();
14713    if recovered_classes.iter().any(|existing| {
14714        existing.class_range == class_range && existing.scope_components == scope_components
14715    }) {
14716        return;
14717    }
14718    recovered_classes.push(CppSentinelRecoveredClass {
14719        namespace_range,
14720        namespace_scope_components: namespace_components.to_vec(),
14721        class_range,
14722        scope_components,
14723        owner_ranges,
14724    });
14725}
14726
14727fn cpp_sentinel_recovered_namespace_components(
14728    function: Node<'_>,
14729    recovered_components: &[String],
14730    source: &str,
14731) -> Vec<String> {
14732    let mut ancestor_components = Vec::new();
14733    let mut ancestor = function.parent();
14734    while let Some(current) = ancestor {
14735        if current.kind() == "namespace_definition"
14736            && let Some(name_node) = current.child_by_field_name("name")
14737            && let Some(components) = cpp_name_components(name_node, source)
14738        {
14739            ancestor_components.push(
14740                components
14741                    .into_iter()
14742                    .map(|component| component.name)
14743                    .collect::<Vec<_>>(),
14744            );
14745        }
14746        ancestor = current.parent();
14747    }
14748    ancestor_components.reverse();
14749    let mut ancestors = ancestor_components
14750        .into_iter()
14751        .flatten()
14752        .collect::<Vec<_>>();
14753
14754    let overlap = (0..=ancestors.len().min(recovered_components.len()))
14755        .rev()
14756        .find(|length| {
14757            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
14758        })
14759        .unwrap_or(0);
14760    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
14761    ancestors
14762}
14763
14764fn cpp_sentinel_recovered_owner_ranges(
14765    body: Node<'_>,
14766    namespace_components: &[String],
14767    source: &str,
14768) -> Vec<CppSentinelRecoveredOwner> {
14769    let mut owners = Vec::new();
14770    walk_named_tree_preorder(body, true, |node| {
14771        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14772    });
14773    owners
14774}
14775
14776fn cpp_sentinel_collect_owner_range(
14777    node: Node<'_>,
14778    namespace_components: &[String],
14779    source: &str,
14780    owners: &mut Vec<CppSentinelRecoveredOwner>,
14781) -> WalkControl {
14782    if node.kind() != "function_definition" {
14783        return WalkControl::Continue;
14784    }
14785    let Some(function_declarator) = extract_function_declarator(node) else {
14786        return WalkControl::Continue;
14787    };
14788    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
14789        return WalkControl::Continue;
14790    };
14791    let Some(mut components) = cpp_name_components(name_node, source) else {
14792        return WalkControl::Continue;
14793    };
14794    if components.len() <= 1 {
14795        return WalkControl::Continue;
14796    }
14797    components.pop();
14798    let mut owner_components = components
14799        .into_iter()
14800        .map(|component| component.name)
14801        .collect::<Vec<_>>();
14802    let overlap = (0..=namespace_components.len().min(owner_components.len()))
14803        .rev()
14804        .find(|length| {
14805            owner_components[..*length]
14806                == namespace_components[namespace_components.len().saturating_sub(*length)..]
14807        })
14808        .unwrap_or(0);
14809    let mut scope_components = namespace_components.to_vec();
14810    scope_components.extend(owner_components.drain(overlap..));
14811    if scope_components.len() <= namespace_components.len() {
14812        return WalkControl::Continue;
14813    }
14814    let range = cpp_declaration_range(node);
14815    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
14816        existing.range == range && existing.scope_components == scope_components
14817    }) {
14818        owners.push(CppSentinelRecoveredOwner {
14819            range,
14820            owner_name_start_byte: name_node.start_byte(),
14821            namespace_component_count: namespace_components.len(),
14822            scope_components,
14823        });
14824    }
14825    WalkControl::Continue
14826}
14827
14828fn cpp_sentinel_extend_unique_owner_ranges(
14829    owners: &mut Vec<CppSentinelRecoveredOwner>,
14830    additional: Vec<CppSentinelRecoveredOwner>,
14831) {
14832    for owner in additional {
14833        if !owners.iter().any(|existing| {
14834            existing.range == owner.range && existing.scope_components == owner.scope_components
14835        }) {
14836            owners.push(owner);
14837        }
14838    }
14839}
14840
14841fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
14842    if node.kind() != "ERROR" || node.named_child_count() != 1 {
14843        return false;
14844    }
14845    let Some(end_name) = node.named_child(0) else {
14846        return false;
14847    };
14848    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
14849        return false;
14850    }
14851    let mut cursor = node.walk();
14852    node.children(&mut cursor)
14853        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
14854}
14855
14856/// Collect owner definitions that the malformed sentinel left as later
14857/// declaration-list siblings. Parser-visible namespace siblings are a hard
14858/// boundary: their declarations must keep their own lexical namespace.
14859fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14860    parent: Node<'_>,
14861    sentinel_node: Node<'_>,
14862    namespace_components: &[String],
14863    source: &str,
14864) -> Vec<CppSentinelRecoveredOwner> {
14865    let mut owners = Vec::new();
14866    let mut after_sentinel = false;
14867    let mut cursor = parent.walk();
14868    for child in parent.named_children(&mut cursor) {
14869        if !after_sentinel {
14870            if same_node(child, sentinel_node) {
14871                after_sentinel = true;
14872            }
14873            continue;
14874        }
14875        walk_named_tree_preorder(child, true, |node| {
14876            if node.kind() == "namespace_definition" {
14877                return WalkControl::SkipChildren;
14878            }
14879            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14880        });
14881    }
14882    owners
14883}
14884
14885/// Collect owner definitions after a malformed namespace, stopping only at
14886/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
14887/// enclosing container is not trusted to belong to the recovered namespace.
14888fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14889    parent: Node<'_>,
14890    sentinel_node: Node<'_>,
14891    namespace_components: &[String],
14892    source: &str,
14893) -> Option<Vec<CppSentinelRecoveredOwner>> {
14894    let mut owners = Vec::new();
14895    let mut after_namespace = false;
14896    let mut cursor = parent.walk();
14897    for child in parent.named_children(&mut cursor) {
14898        if !after_namespace {
14899            if same_node(child, sentinel_node) {
14900                after_namespace = true;
14901            }
14902            continue;
14903        }
14904        if cpp_sentinel_namespace_end(child, source) {
14905            return Some(owners);
14906        }
14907        walk_named_tree_preorder(child, true, |node| {
14908            if node.kind() == "namespace_definition" {
14909                return WalkControl::SkipChildren;
14910            }
14911            cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
14912        });
14913    }
14914    None
14915}
14916
14917fn cpp_sentinel_recovered_sibling_owner_ranges(
14918    sentinel_node: Node<'_>,
14919    namespace_components: &[String],
14920    source: &str,
14921) -> Vec<CppSentinelRecoveredOwner> {
14922    let Some(declaration_list) = sentinel_node
14923        .parent()
14924        .filter(|parent| parent.kind() == "declaration_list")
14925    else {
14926        return Vec::new();
14927    };
14928    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
14929        declaration_list,
14930        sentinel_node,
14931        namespace_components,
14932        source,
14933    );
14934
14935    let Some(namespace) = declaration_list
14936        .parent()
14937        .filter(|parent| parent.kind() == "namespace_definition")
14938    else {
14939        return owners;
14940    };
14941    let Some(outer_parent) = namespace.parent() else {
14942        return owners;
14943    };
14944    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
14945        outer_parent,
14946        namespace,
14947        namespace_components,
14948        source,
14949    ) {
14950        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
14951    }
14952    owners
14953}
14954
14955fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
14956    let mut current = function_declarator.child_by_field_name("declarator")?;
14957    loop {
14958        if let Some(name) = macro_decorated_unqualified_name(current) {
14959            current = name;
14960            continue;
14961        }
14962        if matches!(
14963            current.kind(),
14964            "qualified_identifier"
14965                | "scoped_identifier"
14966                | "scoped_type_identifier"
14967                | "identifier"
14968                | "field_identifier"
14969                | "operator_name"
14970                | "destructor_name"
14971                | "literal_operator_name"
14972        ) {
14973            return Some(current);
14974        }
14975        current = current
14976            .child_by_field_name("declarator")
14977            .or_else(|| current.child_by_field_name("name"))
14978            .or_else(|| last_named_child(current))?;
14979    }
14980}
14981
14982/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
14983/// `::` between the halves. The scope is an attribute-like macro token, not
14984/// a namespace; `Name` is the declared name.
14985///
14986/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
14987/// tree-sitter joins the attribute macro and the constructor name into one
14988/// `qualified_identifier` and marks the separator it had to invent as MISSING.
14989/// That flag is the grammar's own record that no separator is in the source,
14990/// so read it instead of looking for `::` in the node text. A genuine
14991/// `Outer::Inner` keeps a present separator and is declined here, and so is
14992/// the explicit-global `::name`, whose `::` is present and whose scope is
14993/// absent.
14994fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
14995    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
14996        return None;
14997    }
14998    let mut cursor = node.walk();
14999    if node
15000        .children(&mut cursor)
15001        .any(|child| child.kind() == "::" && !child.is_missing())
15002    {
15003        return None;
15004    }
15005    node.child_by_field_name("name")
15006}
15007
15008fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
15009    if let Some(name) = macro_decorated_unqualified_name(node) {
15010        return cpp_name_components(name, source);
15011    }
15012    match node.kind() {
15013        "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
15014            let mut components = match node.child_by_field_name("scope") {
15015                Some(scope) => cpp_name_components(scope, source)?,
15016                None => Vec::new(),
15017            };
15018            let name = node.child_by_field_name("name")?;
15019            components.push(canonical_cpp_qualified_component(name, source)?);
15020            Some(components)
15021        }
15022        _ => Some(vec![canonical_cpp_qualified_component(node, source)?]),
15023    }
15024}
15025
15026fn cpp_sentinel_fragment_boundary<'tree>(
15027    function: Node<'tree>,
15028    class_node: Node<'tree>,
15029    class_body: Node<'tree>,
15030    source: &str,
15031) -> Option<(Node<'tree>, Node<'tree>)> {
15032    let declaration_list = function.parent()?;
15033    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
15034        return None;
15035    }
15036    let namespace = declaration_list.parent()?;
15037    if namespace.kind() != "namespace_definition"
15038        || namespace.child_by_field_name("body") != Some(declaration_list)
15039    {
15040        return None;
15041    }
15042    let mut cursor = declaration_list.walk();
15043    let closes = declaration_list
15044        .children(&mut cursor)
15045        .filter(|child| {
15046            !child.is_named()
15047                && child.kind() == "}"
15048                && child.start_byte() >= function.end_byte()
15049                && child.start_byte() > class_node.end_byte()
15050                && child.start_byte() > class_body.start_byte()
15051        })
15052        .collect::<Vec<_>>();
15053    let [close] = closes.as_slice() else {
15054        return None;
15055    };
15056    let semicolon = namespace.next_named_sibling()?;
15057    if !cpp_is_stray_semicolon(semicolon, source)
15058        || close.end_byte() != namespace.end_byte()
15059        || semicolon.start_byte() < namespace.end_byte()
15060    {
15061        return None;
15062    }
15063    Some((*close, semicolon))
15064}
15065
15066/// Detect the bogus declaration/function tree that tree-sitter recovers for a
15067/// region prefixed by an object-like macro sentinel the parser cannot see
15068/// (issue #941), and return the byte range `[start, end)` of the swallowed
15069/// declaration interior to reparse.
15070///
15071/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
15072/// `function_definition` whose first non-comment named child is the sentinel
15073/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
15074/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
15075/// the real items.
15076/// `start` is the end of the sentinel identifier -- everything after it is the
15077/// genuine source. `end` is the node's end, extended across any trailing empty
15078/// `;` statement the mis-parse displaced past the node (the class/struct closing
15079/// semicolon), so the reparse sees a complete, brace-balanced item.
15080///
15081/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
15082/// node (`has_error`). Unknown annotation/export macros can make a real callable
15083/// error-recovered even though tree-sitter still preserves its declarator, so a
15084/// preserved callable is admitted only when a displaced class keyword precedes
15085/// that declarator. The clean-reparse-to-items gate in
15086/// `cpp_reparsed_items_are_indexable` is the final arbiter.
15087/// Return the reparse start and, when present, the structurally recovered class
15088/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
15089/// separately from the reparse start because an opaque template-declaration
15090/// macro may precede it.
15091fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
15092    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") || !node.has_error()
15093    {
15094        return None;
15095    }
15096    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
15097    // retain a valid function declarator despite the unknown export macro making
15098    // the outer node erroneous. Remember that declarator for the ordering gate
15099    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
15100    // class keyword precedes a spurious callable assembled from a later member.
15101    let mut declarator_cursor = node.walk();
15102    let preserved_callable = node
15103        .children_by_field_name("declarator", &mut declarator_cursor)
15104        .find_map(extract_function_declarator);
15105    // Leading documentation comments are attached to the malformed
15106    // `function_definition` as named children.  They are not part of the
15107    // sentinel prefix, so select the first non-comment child structurally
15108    // rather than requiring the sentinel to be child zero.  This is the shape
15109    // emitted for nlohmann/json's `basic_json`: its class documentation comment
15110    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
15111    // envelope otherwise ends at the first nested union.
15112    let mut cursor = node.walk();
15113    let first = node
15114        .named_children(&mut cursor)
15115        .find(|child| child.kind() != "comment")?;
15116    if first.kind() != "type_identifier" {
15117        return None;
15118    }
15119    let sentinel = normalize_cpp_whitespace(node_text(first, source));
15120    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
15121        return None;
15122    }
15123    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
15124    // makes the trailing sentinel of one region and the leading sentinel of the
15125    // next both land as bare macro-token identifiers ahead of the real content.
15126    // Advance past every leading macro-token identifier so the reparse begins at
15127    // genuine source rather than another sentinel that would re-form the bogus
15128    // shape and fail the reparse gate.
15129    let mut start = first.end_byte();
15130    let mut after_first = false;
15131    let mut cursor = node.walk();
15132    for child in node.named_children(&mut cursor) {
15133        if !after_first {
15134            if same_node(child, first) {
15135                after_first = true;
15136            }
15137            continue;
15138        }
15139        if matches!(child.kind(), "identifier" | "type_identifier")
15140            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(child, source)))
15141        {
15142            start = child.end_byte();
15143        } else {
15144            break;
15145        }
15146    }
15147    // An additional opaque template-declaration macro before a class can be
15148    // folded into the bogus function's qualified declarator.  In that shape
15149    // the macro is not a direct sibling we can skip above; tree-sitter exposes
15150    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
15151    // Reparse from that keyword (or a real preceding `template` keyword) so the
15152    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
15153    // a class nested in a genuine sentinel-wrapped namespace lies after the
15154    // body opening and must not change the established region start.
15155    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
15156    let mut class_start = None;
15157    let mut template_start = None;
15158    let mut stack = vec![node];
15159    while let Some(current) = stack.pop() {
15160        if current.start_byte() >= prefix_end {
15161            continue;
15162        }
15163        if matches!(
15164            current.kind(),
15165            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
15166        ) {
15167            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
15168                "class" | "struct" | "union" | "enum" => {
15169                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
15170                        seen.min(current.start_byte())
15171                    }));
15172                }
15173                "template" => {
15174                    template_start =
15175                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
15176                            seen.min(current.start_byte())
15177                        }));
15178                }
15179                _ => {}
15180            }
15181        }
15182        let mut cursor = current.walk();
15183        stack.extend(current.children(&mut cursor));
15184    }
15185    if preserved_callable.is_some_and(|callable| {
15186        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
15187    }) {
15188        return None;
15189    }
15190    if let Some(class_start) = class_start {
15191        start = template_start
15192            .filter(|template_start| *template_start < class_start)
15193            .unwrap_or(class_start);
15194    }
15195    Some((start, class_start))
15196}
15197
15198/// Locate a sentinel-prefixed class whose malformed declaration was split across
15199/// root-level siblings. The true class close is represented structurally as a
15200/// lone `}` error followed by the class's displaced `;`; nested method/body
15201/// errors are not direct siblings of the sentinel node and therefore cannot
15202/// satisfy this pair.
15203fn cpp_sentinel_macro_class_region<'tree>(
15204    node: Node<'tree>,
15205    source: &str,
15206) -> Option<(usize, usize, usize, usize, usize, usize)> {
15207    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
15208        return None;
15209    };
15210    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
15211        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
15212        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
15213    if class_start >= body_open_start {
15214        return None;
15215    }
15216    let sibling_close = {
15217        let mut sibling = node.next_named_sibling();
15218        let mut found = None;
15219        while let Some(current) = sibling {
15220            let next = current.next_named_sibling();
15221            if cpp_is_stray_close_brace(current, source)
15222                && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
15223            {
15224                let semicolon = next.expect("checked above");
15225                found = Some((
15226                    current.start_byte(),
15227                    semicolon.end_byte(),
15228                    semicolon.end_position().row + 1,
15229                ));
15230                break;
15231            }
15232            sibling = next;
15233        }
15234        found
15235    };
15236    // A stray `};` sibling is this class's close only when the bounded reparse
15237    // agrees the first body-bearing class ENDS there. When the malformed
15238    // envelope swallowed the class's true close, the scan can promote a much
15239    // later scope's close instead -- in protobuf-generated headers
15240    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
15241    // `struct TableStruct_*` paired with the first message class's `};`, making
15242    // the recovered "class body" span whole `namespace {}` blocks and minting
15243    // namespace-scope classes as nested members of the recovered class, which
15244    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
15245    // (#2275). On disagreement, fall through to the suffix-reparse boundary
15246    // below, which derives the close from the class node's own balanced body
15247    // range.
15248    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
15249        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
15250            return false;
15251        };
15252        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
15253        // A region reparse is its own tree and needs its own parent index.
15254        let reparsed_ancestry = ParentIndex::new(tree.root_node());
15255        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
15256            tree.root_node(),
15257            template_node,
15258            source,
15259            &reparsed_ancestry,
15260        ) else {
15261            return false;
15262        };
15263        let body = reparsed_class.body;
15264        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
15265    });
15266    let (class_close_start, class_close_end, class_close_line) =
15267        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
15268            (class_close_start, class_close_end, class_close_line)
15269        } else {
15270            // When the malformed envelope itself is an ERROR, tree-sitter can
15271            // leave the class's balanced close in the source while promoting
15272            // all following members to siblings. Reparse the complete suffix
15273            // and use the first body-bearing class node's own field range as
15274            // the partition boundary. This keeps balancing in tree-sitter and
15275            // preserves the source's original byte offsets.
15276            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
15277            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
15278            // A region reparse is its own tree and needs its own parent index.
15279            let reparsed_ancestry = ParentIndex::new(tree.root_node());
15280            let reparsed_class = cpp_sentinel_reparsed_class(
15281                tree.root_node(),
15282                template_node,
15283                source,
15284                &reparsed_ancestry,
15285            )?;
15286            let body = reparsed_class.body;
15287            let class_close_end = body.end_byte();
15288            let class_close_start = class_close_end.checked_sub(1)?;
15289            let class_close_line = body.end_position().row + 1;
15290            (class_close_start, class_close_end, class_close_line)
15291        };
15292    if class_close_start <= class_start {
15293        return None;
15294    }
15295
15296    // Reparse only far enough to expose the class body opening. This is a
15297    // structured check that the candidate really begins with a body-bearing
15298    // class-like item; the original malformed tree cannot provide that node.
15299    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
15300    let class_root = tree.root_node();
15301    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
15302    // A region reparse is its own tree and needs its own parent index.
15303    let reparsed_ancestry = ParentIndex::new(class_root);
15304    let reparsed_class =
15305        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
15306    let body = reparsed_class.body;
15307    // The class body opening must agree with the malformed wrapper's structured
15308    // body field. This rejects an inner nested class while permitting later
15309    // members to remain fragmented as root-level siblings in the bounded parse.
15310    if body.start_byte() != body_open_start {
15311        return None;
15312    }
15313    let body_start = body.start_byte().checked_add(1)?;
15314    (body_start < class_close_start).then_some((
15315        reparse_start,
15316        class_start,
15317        body_start,
15318        class_close_start,
15319        class_close_end,
15320        class_close_line,
15321    ))
15322}
15323
15324/// Find the `{` token immediately following the class/struct/union/enum token
15325/// at `class_start` in the malformed tree. The token is anonymous in the C++
15326/// grammar, so this deliberately walks all children (not only named children)
15327/// and relies on sibling structure rather than source-text searching.
15328fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
15329    let mut stack = vec![node];
15330    while let Some(current) = stack.pop() {
15331        if current.start_byte() == class_start
15332            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
15333        {
15334            let mut sibling = current.next_sibling();
15335            while let Some(candidate) = sibling {
15336                if candidate.kind() == "{" {
15337                    return Some(candidate.start_byte());
15338                }
15339                sibling = candidate.next_sibling();
15340            }
15341        }
15342        let mut cursor = current.walk();
15343        stack.extend(current.children(&mut cursor));
15344    }
15345    None
15346}
15347
15348/// The class body that tree-sitter displaced out of a sentinel-prefixed
15349/// declaration and left as the malformed node's next sibling.
15350///
15351/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
15352/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
15353/// the last child of that `ERROR` and its `{` opens a sibling
15354/// `compound_statement` instead. The body is still the malformed tree's own
15355/// structured token, which is what the caller's `body.start_byte() !=
15356/// body_open_start` agreement check needs; it just is not reachable by walking
15357/// forward from the class token inside the node.
15358fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
15359    node.next_named_sibling()
15360        .filter(|sibling| sibling.kind() == "compound_statement")
15361}
15362
15363fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
15364    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
15365    let mut end = if class_start.is_some() {
15366        cpp_macro_prefixed_class_end(source, start)?
15367    } else {
15368        node.end_byte()
15369    };
15370    if class_start.is_none()
15371        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
15372    {
15373        end = end.max(namespace_end);
15374    }
15375    let mut sibling = node.next_named_sibling();
15376    while let Some(current) = sibling {
15377        if !cpp_is_stray_semicolon(current, source) {
15378            break;
15379        }
15380        end = current.end_byte();
15381        sibling = current.next_named_sibling();
15382    }
15383    (start < end).then_some((start, end))
15384}
15385
15386/// Extend a sentinel reparse through a following namespace that tree-sitter
15387/// flattened into the sentinel node's sibling list.
15388///
15389/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
15390/// unknown macro becomes a false function return type and consumes the first
15391/// namespace body. A second `namespace detail` then loses its enclosing node:
15392/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
15393/// attaches its declarations to the surrounding error tree. Reparse from that
15394/// structured keyword so tree-sitter, rather than a source-text brace scan,
15395/// supplies the complete namespace boundary.
15396fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
15397    let mut sibling = node.next_sibling();
15398    let keyword = loop {
15399        let candidate = sibling?;
15400        sibling = candidate.next_sibling();
15401        if candidate.kind() != "comment" {
15402            break candidate;
15403        }
15404    };
15405    if keyword.kind() != "namespace" {
15406        return None;
15407    }
15408    let name = loop {
15409        let candidate = sibling?;
15410        sibling = candidate.next_sibling();
15411        if candidate.kind() != "comment" {
15412            break candidate;
15413        }
15414    };
15415    if cpp_namespace_name_components(name, source).is_empty() {
15416        return None;
15417    }
15418    let open = loop {
15419        let candidate = sibling?;
15420        sibling = candidate.next_sibling();
15421        if candidate.kind() != "comment" {
15422            break candidate;
15423        }
15424    };
15425    if open.kind() != "{" {
15426        return None;
15427    }
15428
15429    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
15430    let root = tree.root_node();
15431    let mut cursor = root.walk();
15432    let namespace = root
15433        .named_children(&mut cursor)
15434        .find(|candidate| candidate.kind() != "comment")?;
15435    (namespace.kind() == "namespace_definition"
15436        && namespace.start_byte() == keyword.start_byte()
15437        && namespace.child_by_field_name("body").is_some())
15438    .then_some(namespace.end_byte())
15439}
15440
15441/// Parse the source suffix beginning at a structurally recovered class/template
15442/// keyword and return the end of its first body-bearing class item.  The parser,
15443/// rather than a brace scanner, owns nested-body balancing.  This is needed when
15444/// the original error tree truncates the class and scatters later members as
15445/// top-level siblings.
15446fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
15447    let tree = cpp_reparse_region_items(source, start, source.len())?;
15448    let root = tree.root_node();
15449    let mut cursor = root.walk();
15450    for item in root.named_children(&mut cursor) {
15451        if item.end_byte() <= start || item.kind() == "comment" {
15452            continue;
15453        }
15454        let mut stack = vec![item];
15455        while let Some(current) = stack.pop() {
15456            if matches!(
15457                current.kind(),
15458                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
15459            ) && cpp_body_node(current).is_some()
15460            {
15461                return Some(current.end_byte());
15462            }
15463            let mut cursor = current.walk();
15464            stack.extend(current.named_children(&mut cursor));
15465        }
15466        // The recovered prefix is required to begin with the class item.  If
15467        // the first real item is something else, fail closed rather than skip
15468        // arbitrary source looking for a later class.
15469        return None;
15470    }
15471    None
15472}
15473
15474/// An empty `;` statement: the displaced closing semicolon of a struct/class that
15475/// the sentinel mis-parse split off past the bogus function node.
15476fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
15477    node.kind() == "expression_statement"
15478        && node.named_child_count() == 0
15479        && node_text(node, source).trim() == ";"
15480}
15481
15482/// Recover the member that follows CPython's object header macro when
15483/// tree-sitter folds both declarations into one malformed field.
15484///
15485/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
15486/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
15487/// sole child is the real field declarator. The tail may wrap that identifier
15488/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
15489/// malformed wrappers do not provide the same evidence that this is the
15490/// CPython macro boundary.
15491#[derive(Clone, Copy)]
15492pub(crate) struct RecoveredPyObjectHeadField<'tree> {
15493    pub(crate) type_node: Node<'tree>,
15494    pub(crate) name: Node<'tree>,
15495    pub(crate) declarator: Node<'tree>,
15496}
15497
15498impl RecoveredPyObjectHeadField<'_> {
15499    pub(crate) fn pointer_depth(self) -> i32 {
15500        let mut depth = 0;
15501        let mut current = self.declarator;
15502        while current != self.name {
15503            debug_assert_eq!(current.kind(), "pointer_declarator");
15504            depth += 1;
15505            current = current
15506                .child_by_field_name("declarator")
15507                .expect("recovered PyObject field pointer has an inner declarator");
15508        }
15509        depth
15510    }
15511}
15512
15513pub(crate) fn recovered_pyobject_head_field<'tree>(
15514    node: Node<'tree>,
15515    source: &str,
15516) -> Option<RecoveredPyObjectHeadField<'tree>> {
15517    if node.kind() != "field_declaration" {
15518        return None;
15519    }
15520    let type_node = node.child_by_field_name("type")?;
15521    if type_node.kind() != "type_identifier"
15522        || node_text(type_node, source).trim() != "PyObject_HEAD"
15523    {
15524        return None;
15525    }
15526    let pseudo_declarator = node.child_by_field_name("declarator")?;
15527    let mut cursor = node.walk();
15528    let errors = node
15529        .named_children(&mut cursor)
15530        .filter(|child| child.kind() == "ERROR")
15531        .collect::<Vec<_>>();
15532    let [error] = errors.as_slice() else {
15533        return None;
15534    };
15535    if error.named_child_count() != 1 {
15536        return None;
15537    }
15538    let error_child = error.named_child(0)?;
15539    if pseudo_declarator.kind() == "field_identifier"
15540        && error.start_byte() >= pseudo_declarator.end_byte()
15541        && error_child.kind() == "identifier"
15542    {
15543        return Some(RecoveredPyObjectHeadField {
15544            type_node: pseudo_declarator,
15545            name: error_child,
15546            declarator: error_child,
15547        });
15548    }
15549    if pseudo_declarator.kind() != "pointer_declarator"
15550        || error.end_byte() > pseudo_declarator.start_byte()
15551        || error_child.kind() != "identifier"
15552    {
15553        return None;
15554    }
15555    let mut name = pseudo_declarator;
15556    while name.kind() == "pointer_declarator" {
15557        name = name.child_by_field_name("declarator")?;
15558    }
15559    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
15560        type_node: error_child,
15561        name,
15562        declarator: pseudo_declarator,
15563    })
15564}
15565
15566/// Recover the real field name when a leading object-like annotation macro
15567/// displaces a qualified type into tree-sitter's bit-field recovery shape.
15568///
15569/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
15570/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
15571/// `bitfield_clause` containing an error plus an assignment.  The assignment's
15572/// left field is the only structured declaration name in that malformed tail.
15573/// A real bit-field is excluded by the all-caps macro type and required error.
15574fn recovered_macro_qualified_field_declarators<'tree>(
15575    node: Node<'tree>,
15576    source: &str,
15577) -> Option<Vec<Node<'tree>>> {
15578    if node.kind() != "field_declaration" {
15579        return None;
15580    }
15581    let macro_type = node.child_by_field_name("type")?;
15582    if macro_type.kind() != "type_identifier"
15583        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15584    {
15585        return None;
15586    }
15587    let pseudo_declarator = node.child_by_field_name("declarator")?;
15588    if pseudo_declarator.kind() != "field_identifier" {
15589        return None;
15590    }
15591    let mut cursor = node.walk();
15592    let clause = node
15593        .named_children(&mut cursor)
15594        .find(|child| child.kind() == "bitfield_clause")?;
15595    if !(0..clause.named_child_count()).any(|index| {
15596        clause
15597            .named_child(index)
15598            .is_some_and(|child| child.kind() == "ERROR")
15599    }) {
15600        return None;
15601    }
15602    let mut recovered = Vec::new();
15603    let mut stack = vec![clause];
15604    while let Some(current) = stack.pop() {
15605        if current.kind() == "assignment_expression"
15606            && let Some(left) = current.child_by_field_name("left")
15607            && extract_variable_name(left, source).is_some()
15608        {
15609            recovered.push(left);
15610            break;
15611        }
15612        let mut cursor = current.walk();
15613        stack.extend(current.named_children(&mut cursor));
15614    }
15615    if recovered.is_empty() {
15616        return None;
15617    }
15618    let mut cursor = node.walk();
15619    recovered.extend(
15620        node.children_by_field_name("declarator", &mut cursor)
15621            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
15622    );
15623    Some(recovered)
15624}
15625
15626/// Recover a macro-qualified constructor that tree-sitter represents as one
15627/// field declaration. The constructor call remains inside the direct recovery
15628/// error, while each member initializer becomes a false function declarator.
15629/// The class owner proves the constructor name and lets the caller ignore those
15630/// initializer declarators.
15631fn recovered_macro_qualified_constructor_call<'tree>(
15632    node: Node<'tree>,
15633    class_name: &str,
15634    source: &str,
15635) -> Option<Node<'tree>> {
15636    if node.kind() != "field_declaration" {
15637        return None;
15638    }
15639    let macro_type = node.child_by_field_name("type")?;
15640    if macro_type.kind() != "type_identifier"
15641        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15642    {
15643        return None;
15644    }
15645    let mut cursor = node.walk();
15646    let bitfield = node
15647        .named_children(&mut cursor)
15648        .find(|child| child.kind() == "bitfield_clause")?;
15649    let error = bitfield
15650        .named_child(0)
15651        .filter(|child| child.kind() == "ERROR")?;
15652    let mut stack = vec![error];
15653    while let Some(current) = stack.pop() {
15654        if current.kind() == "call_expression"
15655            && current
15656                .child_by_field_name("function")
15657                .is_some_and(|function| node_text(function, source) == class_name)
15658            && current
15659                .child_by_field_name("arguments")
15660                .is_some_and(|arguments| arguments.kind() == "argument_list")
15661        {
15662            return Some(current);
15663        }
15664        let mut cursor = current.walk();
15665        stack.extend(current.named_children(&mut cursor));
15666    }
15667    None
15668}
15669
15670/// Recover a macro-qualified member function declaration that tree-sitter
15671/// represents as a pseudo-field. An object-like export macro before a qualified
15672/// return type can displace the namespace and type into an ERROR/bitfield
15673/// recovery, leaving the callable as a structured `call_expression`.
15674///
15675/// The caller must route this shape before ordinary declarator classification;
15676/// otherwise the displaced namespace identifier is published as a field.
15677fn recovered_macro_qualified_function_call<'tree>(
15678    node: Node<'tree>,
15679    source: &str,
15680) -> Option<Node<'tree>> {
15681    if node.kind() != "field_declaration" {
15682        return None;
15683    }
15684    let macro_type = node.child_by_field_name("type")?;
15685    if macro_type.kind() != "type_identifier"
15686        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15687    {
15688        return None;
15689    }
15690    let declarator = node.child_by_field_name("declarator")?;
15691    if declarator.kind() != "field_identifier" {
15692        return None;
15693    }
15694    let mut cursor = node.walk();
15695    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
15696    if !named.iter().any(|child| {
15697        child.kind() == "storage_class_specifier"
15698            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
15699    }) {
15700        return None;
15701    }
15702    let bitfield = named
15703        .iter()
15704        .find(|child| child.kind() == "bitfield_clause")?;
15705    let mut bitfield_cursor = bitfield.walk();
15706    let payload = bitfield
15707        .named_children(&mut bitfield_cursor)
15708        .collect::<Vec<_>>();
15709    let [displaced_error, call] = payload.as_slice() else {
15710        return None;
15711    };
15712    if displaced_error.kind() != "ERROR"
15713        || displaced_error.named_child_count() != 1
15714        || displaced_error
15715            .named_child(0)
15716            .is_none_or(|child| child.kind() != "identifier")
15717        || call.kind() != "call_expression"
15718        || call
15719            .child_by_field_name("function")
15720            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
15721        || call
15722            .child_by_field_name("arguments")
15723            .is_none_or(|arguments| arguments.kind() != "argument_list")
15724    {
15725        return None;
15726    }
15727    Some(*call)
15728}
15729
15730fn recovered_macro_qualified_function_parameters(
15731    arguments: Node<'_>,
15732    source: &str,
15733) -> Option<(String, Vec<String>)> {
15734    if arguments.kind() != "argument_list" {
15735        return None;
15736    }
15737    let mut cursor = arguments.walk();
15738    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
15739    if named.is_empty() {
15740        return Some(("()".to_string(), Vec::new()));
15741    }
15742    let mut types = Vec::new();
15743    let mut labels = Vec::new();
15744    let mut index = 0;
15745    while index < named.len() {
15746        let parameter_type = named[index];
15747        let parameter_name = named.get(index + 1).copied()?;
15748        if !matches!(
15749            parameter_type.kind(),
15750            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
15751        ) || parameter_name.kind() != "ERROR"
15752            || parameter_name.named_child_count() != 1
15753            || parameter_name
15754                .named_child(0)
15755                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
15756        {
15757            return None;
15758        }
15759        let parameter_name = parameter_name.named_child(0)?;
15760        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
15761        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
15762        index += 2;
15763    }
15764    Some((format!("({})", types.join(", ")), labels))
15765}
15766
15767/// Recognize the phantom field tree-sitter emits for a macro-qualified
15768/// function return type.  For example,
15769/// `static API result_type ThresholdForSmallA() { ... }` can become a
15770/// `field_declaration` (`API` as the type and `result_type` as a field name)
15771/// followed by a clean `function_definition` for `ThresholdForSmallA`.
15772///
15773/// Keep this predicate entirely tied to the CST envelope: the type must be an
15774/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
15775/// the declaration must carry a missing semicolon rather than a real one, and
15776/// the immediate named sibling must expose a function declarator.  A real
15777/// macro-decorated field with an explicit semicolon therefore remains a field.
15778pub fn recovered_macro_return_type_node<'tree>(
15779    node: Node<'tree>,
15780    source: &str,
15781) -> Option<Node<'tree>> {
15782    if node.kind() != "field_declaration" {
15783        return None;
15784    }
15785    let macro_type = node.child_by_field_name("type")?;
15786    if macro_type.kind() != "type_identifier"
15787        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
15788    {
15789        return None;
15790    }
15791    let declarator = node.child_by_field_name("declarator")?;
15792    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
15793        return None;
15794    }
15795    let mut has_missing_semicolon = false;
15796    let mut has_real_semicolon = false;
15797    for child in children_iter(node) {
15798        if child.kind() != ";" {
15799            continue;
15800        }
15801        if child.is_missing() {
15802            has_missing_semicolon = true;
15803        } else {
15804            has_real_semicolon = true;
15805        }
15806    }
15807    if !has_missing_semicolon || has_real_semicolon {
15808        return None;
15809    }
15810    let mut next = node.next_named_sibling();
15811    while next.is_some_and(|sibling| sibling.kind() == "comment") {
15812        next = next.and_then(|sibling| sibling.next_named_sibling());
15813    }
15814    let next = next?;
15815    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
15816        return None;
15817    }
15818    let function_declarator = next.child_by_field_name("declarator")?;
15819    extract_function_declarator(function_declarator).map(|_| declarator)
15820}
15821
15822/// Whether `name` is a type parameter of a template declaration that lexically
15823/// encloses `node`. The malformed macro-return field uses the parameter name as
15824/// its pseudo-declarator; preserving that field is necessary to publish a
15825/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
15826/// ancestors instead of interpreting source text so nested templates and
15827/// parser-recovered regions retain their real lexical scopes.
15828pub(crate) fn cpp_active_template_type_parameter<'tree>(
15829    node: Node<'tree>,
15830    name: &str,
15831    source: &str,
15832    ancestry: &ParentIndex<'tree>,
15833) -> bool {
15834    let mut ancestor = ancestry.parent(node);
15835    while let Some(current) = ancestor {
15836        if current.kind() == "template_declaration"
15837            && let Some(parameters) = current.child_by_field_name("parameters")
15838        {
15839            let mut cursor = parameters.walk();
15840            if parameters.named_children(&mut cursor).any(|parameter| {
15841                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15842                    && cpp_template_parameter_name(parameter, source)
15843                        .is_some_and(|parameter_name| parameter_name == name)
15844            }) {
15845                return true;
15846            }
15847        }
15848        ancestor = ancestry.parent(current);
15849    }
15850    false
15851}
15852
15853/// Reparse the region `[start, end)` of `source` as C++, confined to the region
15854/// via included ranges so every reparsed node keeps its original byte offset and
15855/// line number. The existing visitors read node text from the original source,
15856/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
15857/// `parse_rust_region_tree` technique.
15858fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
15859    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
15860}
15861
15862fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
15863    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
15864        return None;
15865    }
15866    let semicolon = node.next_sibling()?;
15867    if semicolon.kind() != ";" || semicolon.is_missing() {
15868        return None;
15869    }
15870    let row = node.start_position().row;
15871    let mut start = node.start_byte();
15872    let mut sibling = node.prev_sibling();
15873    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
15874        if previous.kind() == ";" {
15875            break;
15876        }
15877        start = previous.start_byte();
15878        sibling = previous.prev_sibling();
15879    }
15880    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
15881}
15882
15883/// One `RET name _(( args ));`-style K&R prototype-macro invocation
15884/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
15885/// byte spans a clean reparse needs, in original-source order.
15886struct PrototypeMacroCandidate {
15887    /// Where the return type and declared name begin.
15888    run_start: usize,
15889    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
15890    /// exclusive end of the first reparse span.
15891    identifier_start: usize,
15892    /// The inner `(`'s start byte: the start of the second reparse span.
15893    /// This paren is kept so the reparse sees exactly one parameter-list
15894    /// paren pair.
15895    inner_open_start: usize,
15896    /// Byte just past the inner `)`: the end of the second reparse span.
15897    inner_close_end: usize,
15898    /// Byte just past the outer `)`: the start of the third reparse span.
15899    outer_close_end: usize,
15900    /// Byte just past the terminating `;`: the end of the third reparse
15901    /// span, and of the whole candidate.
15902    semicolon_end: usize,
15903}
15904
15905impl PrototypeMacroCandidate {
15906    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
15907    /// one included-range tree: the return type and name, the parenthesized
15908    /// argument list, and the terminating `;`. The macro token and its outer
15909    /// wrapping parenthesis are omitted -- deleting them is exactly what the
15910    /// (absent) preprocessor macro expansion would do.
15911    fn ranges(&self) -> [(usize, usize); 3] {
15912        [
15913            (self.run_start, self.identifier_start),
15914            (self.inner_open_start, self.inner_close_end),
15915            (self.outer_close_end, self.semicolon_end),
15916        ]
15917    }
15918}
15919
15920/// A live terminating semicolon owned directly by `node`.
15921fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
15922    node.child(node.child_count().checked_sub(1)?)
15923        .filter(|child| child.kind() == ";" && !child.is_missing())
15924}
15925
15926/// The known pre-ANSI prototype macros whose expansion is exactly their one
15927/// argument. The malformed CST cannot prove that an arbitrary identifier has
15928/// that definition, so unknown spellings fail closed rather than turning an
15929/// ordinary broken declaration into a Function (issue #2932).
15930fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
15931    matches!(
15932        node.kind(),
15933        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
15934    ) && matches!(
15935        normalize_cpp_whitespace(node_text(node, source)).as_str(),
15936        "_" | "__P" | "OF" | "PROTO"
15937    )
15938}
15939
15940/// Read tree-sitter's recovery for `declared_name MACRO`: a
15941/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
15942/// declared name, and whose `name` is a known prototype macro.
15943fn cpp_prototype_macro_qualified_parts<'tree>(
15944    node: Node<'tree>,
15945    source: &str,
15946) -> Option<(Node<'tree>, Node<'tree>)> {
15947    if node.kind() != "qualified_identifier" {
15948        return None;
15949    }
15950    let declared_name = node
15951        .child_by_field_name("scope")
15952        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
15953    let macro_name = macro_decorated_unqualified_name(node)?;
15954    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
15955}
15956
15957/// The inner parenthesized node of tree-sitter's structured
15958/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
15959/// the macro invocation's outer parentheses while retaining the one pair an
15960/// ordinary function declarator needs. No delimiter search is involved: both
15961/// pairs and their ownership come from the CST.
15962fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
15963    if arguments.kind() != "argument_list"
15964        || arguments.named_child_count() != 1
15965        || arguments.child_count() != 3
15966        || arguments
15967            .child(0)
15968            .is_none_or(|open| open.kind() != "(" || open.is_missing())
15969        || arguments
15970            .child(2)
15971            .is_none_or(|close| close.kind() != ")" || close.is_missing())
15972    {
15973        return None;
15974    }
15975    let inner = arguments.named_child(0)?;
15976    let close_index = match inner.kind() {
15977        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
15978        // tree-sitter completes the C++ cast production with a zero-width
15979        // value after the live close parenthesis.
15980        "cast_expression" => inner.child_count().checked_sub(2)?,
15981        _ => return None,
15982    };
15983    (inner
15984        .child(0)
15985        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
15986        && inner
15987            .child(close_index)
15988            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
15989    .then_some(inner)
15990}
15991
15992fn cpp_prototype_macro_candidate_from_init_declaration(
15993    declaration: Node<'_>,
15994    source: &str,
15995) -> Option<PrototypeMacroCandidate> {
15996    let init = declaration
15997        .child_by_field_name("declarator")
15998        .filter(|declarator| declarator.kind() == "init_declarator")?;
15999    let malformed_declarator = init.child_by_field_name("declarator")?;
16000    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
16001        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
16002    } else {
16003        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
16004            return None;
16005        }
16006        let declared_name_error = init
16007            .prev_named_sibling()
16008            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
16009        let declared_name = declared_name_error
16010            .named_child(0)
16011            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
16012        (declared_name, malformed_declarator)
16013    };
16014    let arguments = init
16015        .child_by_field_name("value")
16016        .filter(|value| value.kind() == "argument_list")?;
16017    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
16018    let semicolon = cpp_direct_semicolon(declaration)?;
16019    let return_type = declaration.child_by_field_name("type")?;
16020    if return_type.end_byte() > declared_name.start_byte()
16021        || declared_name.end_byte() > macro_name.start_byte()
16022        || macro_name.end_byte() > arguments.start_byte()
16023        || arguments.end_byte() > semicolon.start_byte()
16024    {
16025        return None;
16026    }
16027    Some(PrototypeMacroCandidate {
16028        run_start: declaration.start_byte(),
16029        identifier_start: macro_name.start_byte(),
16030        inner_open_start: inner.start_byte(),
16031        inner_close_end: inner.end_byte(),
16032        outer_close_end: arguments.end_byte(),
16033        semicolon_end: semicolon.end_byte(),
16034    })
16035}
16036
16037fn cpp_prototype_macro_candidate_from_qualified_declaration(
16038    declaration: Node<'_>,
16039    source: &str,
16040) -> Option<PrototypeMacroCandidate> {
16041    let qualified = declaration
16042        .child_by_field_name("declarator")
16043        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
16044    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
16045    let open_error = qualified
16046        .next_named_sibling()
16047        .filter(|next| next.kind() == "ERROR")?;
16048    let close_error = last_named_child(declaration)
16049        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
16050    if open_error.child_count() < 3
16051        || open_error
16052            .child(0)
16053            .is_none_or(|open| open.kind() != "(" || open.is_missing())
16054        || open_error
16055            .child(1)
16056            .is_none_or(|open| open.kind() != "(" || open.is_missing())
16057        || close_error.child_count() != 2
16058        || close_error
16059            .child(0)
16060            .is_none_or(|close| close.kind() != ")" || close.is_missing())
16061        || close_error
16062            .child(1)
16063            .is_none_or(|close| close.kind() != ")" || close.is_missing())
16064    {
16065        return None;
16066    }
16067    let inner_open = open_error.child(1)?;
16068    let inner_close = close_error.child(0)?;
16069    let outer_close = close_error.child(1)?;
16070    let semicolon = cpp_direct_semicolon(declaration)?;
16071    let return_type = declaration.child_by_field_name("type")?;
16072    if return_type.end_byte() > qualified.start_byte()
16073        || macro_name.end_byte() > open_error.start_byte()
16074        || inner_open.start_byte() > inner_close.end_byte()
16075        || inner_close.end_byte() > outer_close.start_byte()
16076        || outer_close.end_byte() > semicolon.start_byte()
16077    {
16078        return None;
16079    }
16080    Some(PrototypeMacroCandidate {
16081        run_start: declaration.start_byte(),
16082        identifier_start: macro_name.start_byte(),
16083        inner_open_start: inner_open.start_byte(),
16084        inner_close_end: inner_close.end_byte(),
16085        outer_close_end: outer_close.end_byte(),
16086        semicolon_end: semicolon.end_byte(),
16087    })
16088}
16089
16090fn cpp_prototype_macro_candidate_from_pointer_expression(
16091    statement: Node<'_>,
16092    source: &str,
16093) -> Option<PrototypeMacroCandidate> {
16094    if statement.kind() != "expression_statement"
16095        || statement.named_child_count() != 1
16096        || !statement.has_error()
16097    {
16098        return None;
16099    }
16100    let expansion = statement
16101        .named_child(0)
16102        .filter(|child| child.kind() == "parameter_pack_expansion")?;
16103    let binary = expansion
16104        .child_by_field_name("pattern")
16105        .filter(|pattern| pattern.kind() == "binary_expression")?;
16106    if binary.child_count() != 3
16107        || binary
16108            .child(1)
16109            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
16110        || expansion
16111            .child(expansion.child_count().checked_sub(1)?)
16112            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
16113    {
16114        return None;
16115    }
16116    let return_type = binary.child_by_field_name("left")?;
16117    let call = binary
16118        .child_by_field_name("right")
16119        .filter(|right| right.kind() == "call_expression")?;
16120    let qualified = call.child_by_field_name("function")?;
16121    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
16122    let arguments = call
16123        .child_by_field_name("arguments")
16124        .filter(|arguments| arguments.kind() == "argument_list")?;
16125    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
16126    let semicolon = cpp_direct_semicolon(statement)?;
16127    if return_type.end_byte() > declared_name.start_byte()
16128        || macro_name.end_byte() > arguments.start_byte()
16129        || arguments.end_byte() > semicolon.start_byte()
16130    {
16131        return None;
16132    }
16133    Some(PrototypeMacroCandidate {
16134        run_start: statement.start_byte(),
16135        identifier_start: macro_name.start_byte(),
16136        inner_open_start: inner.start_byte(),
16137        inner_close_end: inner.end_byte(),
16138        outer_close_end: arguments.end_byte(),
16139        semicolon_end: semicolon.end_byte(),
16140    })
16141}
16142
16143/// Recover only CST shapes whose fields preserve the declaration name, macro
16144/// invocation, nested argument list, and terminator. Arbitrary flattened
16145/// `ERROR` wreckage stays unsupported rather than being interpreted through a
16146/// token or delimiter scan (issue #2932).
16147fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
16148    let candidate = match node.kind() {
16149        "declaration" if node.has_error() => {
16150            cpp_prototype_macro_candidate_from_init_declaration(node, source)
16151                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
16152        }
16153        "expression_statement" => {
16154            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
16155        }
16156        _ => None,
16157    };
16158    candidate.into_iter().collect()
16159}
16160
16161fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
16162    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
16163        return false;
16164    }
16165    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
16166        return false;
16167    }
16168    let Some(declarator) = (if node.kind() == "function_definition" {
16169        node.child_by_field_name("declarator")
16170            .and_then(extract_function_declarator)
16171    } else {
16172        node.named_child(0)
16173            .filter(|child| child.kind() == "function_declarator")
16174    }) else {
16175        return false;
16176    };
16177    let Some(name) = cpp_function_declarator_name_node(declarator) else {
16178        return false;
16179    };
16180    declarator.start_byte() == node.start_byte()
16181        && name.kind() == "identifier"
16182        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
16183}
16184
16185/// Reparse a fragmented class-body interior while preserving its original byte
16186/// and line offsets, confined to the region by tree-sitter included ranges.
16187///
16188/// This used to materialize the region's whole file prefix as whitespace and
16189/// make the lexer walk it, the technique #1309 replaced on the other reparse
16190/// path: O(file) per fragmented-class recovery, on files that are already
16191/// error-recovered and already slow (#2788). Included ranges give the parser
16192/// the same view -- the region's bytes, at their original offsets and
16193/// line/column positions -- without materializing or lexing anything before it.
16194///
16195/// The equality of the two views is the claim, so a debug build parses both and
16196/// asserts the trees agree node for node.
16197fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
16198    let region = cpp_reparse_region_items(source, start, end);
16199
16200    #[cfg(debug_assertions)]
16201    assert_eq!(
16202        region.as_ref().map(cpp_tree_shape),
16203        cpp_reparse_padded_class_body(source, start, end)
16204            .as_ref()
16205            .map(cpp_tree_shape),
16206        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
16207         prefix produces"
16208    );
16209
16210    region
16211}
16212
16213/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
16214/// replaces, kept as the oracle a debug build asserts every region reparse
16215/// against and as the release-mode parity tests' reference (#2788).
16216#[cfg(any(debug_assertions, test))]
16217fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
16218    if start >= end {
16219        // The included-range parser refuses an empty region; the padded one
16220        // returned a tree holding nothing, which every caller read as "no
16221        // members here".
16222        return None;
16223    }
16224    let bytes = source.as_bytes();
16225    let prefix = bytes.get(..start)?;
16226    let interior = bytes.get(start..end)?;
16227    let mut padded = Vec::with_capacity(end);
16228    padded.extend(
16229        prefix
16230            .iter()
16231            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
16232    );
16233    padded.extend_from_slice(interior);
16234    let padded = String::from_utf8(padded).ok()?;
16235    let mut parser = Parser::new();
16236    parser
16237        .set_language(&tree_sitter_cpp::LANGUAGE.into())
16238        .ok()?;
16239    parser.parse(&padded, None)
16240}
16241
16242/// Every node of `tree` in preorder, by kind, span and position, which is what
16243/// two reparses of one region have to agree on for their callers to read the
16244/// same declarations out of either (#2788).
16245#[cfg(any(debug_assertions, test))]
16246fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
16247    let mut shape = Vec::new();
16248    let mut cursor = tree.root_node().walk();
16249    let mut stack = vec![tree.root_node()];
16250    while let Some(node) = stack.pop() {
16251        shape.push((
16252            node.kind(),
16253            node.start_byte(),
16254            node.end_byte(),
16255            node.start_position().row,
16256            node.start_position().column,
16257            node.is_named(),
16258            node.is_missing(),
16259        ));
16260        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
16261        stack.extend(children.into_iter().rev());
16262    }
16263    shape
16264}
16265
16266/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
16267/// reparsed interior is indexed only when every top-level named node is a
16268/// well-formed C++ item (or a comment) and at least one real item is present.
16269/// Expression/statement soup surfaces as a top-level `ERROR` or
16270/// `expression_statement`, neither of which is an item kind, so it is rejected.
16271///
16272/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
16273/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
16274/// swallowed by a preceding dangling sentinel) reparses to a real
16275/// `namespace_definition` whose body still holds a bogus `function_definition`,
16276/// so the subtree legitimately carries an error. Container items are admitted
16277/// even with an internal error; the inner bogus function is recovered recursively
16278/// when `visit_function_definition` walks it. Each recursion strips at least one
16279/// leading sentinel, so the region strictly shrinks and recovery terminates.
16280///
16281/// A top-level `function_definition` is the one place we stay strict: it is
16282/// admitted only when it is clean or is itself a sentinel candidate. A function
16283/// that has an error and is not a sentinel is a real callable with a broken body,
16284/// so we refuse the whole reparse and let the ordinary path handle it (preserving
16285/// its real return type rather than re-deriving an implicit one).
16286fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
16287    let mut cursor = root.walk();
16288    let mut saw_item = false;
16289    for child in root.named_children(&mut cursor) {
16290        match child.kind() {
16291            "comment" => {}
16292            "function_definition" => {
16293                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
16294                    return false;
16295                }
16296                saw_item = true;
16297            }
16298            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
16299            _ => return false,
16300        }
16301    }
16302    saw_item
16303}
16304
16305/// Robustness gate for a reparsed fragmented multiple-base export class body
16306/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
16307/// kinds a class body produces when reparsed at translation-unit scope: the
16308/// access-specifier label preceding the first member surfaces as a
16309/// `labeled_statement` wrapping that member, and members surface as
16310/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
16311/// Statement or expression soup surfaces as other top-level kinds and is rejected,
16312/// so only a genuinely member-shaped body is ever re-owned as members; anything
16313/// ambiguous falls back to indexing the class alone.
16314fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
16315    if node.kind() != "ERROR" {
16316        return false;
16317    }
16318    let mut stack = Vec::new();
16319    let mut saw_function_declarator = false;
16320    let mut cursor = node.walk();
16321    for child in node.named_children(&mut cursor) {
16322        stack.push(child);
16323    }
16324    while let Some(current) = stack.pop() {
16325        match current.kind() {
16326            // Tree-sitter may wrap adjacent copy-control declarations in a
16327            // nested ERROR. Keep descending only through ERROR wrappers; the
16328            // actual declaration payload must be a function_declarator.
16329            "ERROR" => {
16330                let mut cursor = current.walk();
16331                stack.extend(current.named_children(&mut cursor));
16332            }
16333            "function_declarator" => saw_function_declarator = true,
16334            _ => return false,
16335        }
16336    }
16337    saw_function_declarator
16338}
16339
16340fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
16341    if node.kind() != "ERROR" {
16342        return false;
16343    }
16344    let mut cursor = node.walk();
16345    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16346    let [explicit, constructor_error, destructor] = named.as_slice() else {
16347        return false;
16348    };
16349    let Some(constructor) = constructor_error.named_child(0) else {
16350        return false;
16351    };
16352    let Some(constructor_name) =
16353        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
16354    else {
16355        return false;
16356    };
16357    let Some(destructor_name) =
16358        extract_function_declarator(*destructor).and_then(cpp_function_declarator_name_node)
16359    else {
16360        return false;
16361    };
16362    let Some(destroyed_type) = destructor_name.named_child(0) else {
16363        return false;
16364    };
16365    explicit.kind() == "explicit_function_specifier"
16366        && constructor_error.kind() == "ERROR"
16367        && constructor_error.named_child_count() == 1
16368        && constructor.kind() == "function_declarator"
16369        && constructor_name.kind() == "identifier"
16370        && destructor.kind() == "function_declarator"
16371        && destructor_name.kind() == "destructor_name"
16372        && destroyed_type.kind() == "identifier"
16373        && node_text(constructor_name, source) == node_text(destroyed_type, source)
16374}
16375
16376fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
16377    if node.kind() != "compound_statement" {
16378        return false;
16379    }
16380    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
16381        return false;
16382    };
16383    if prefix.kind() == "labeled_statement"
16384        && prefix.named_child(0).is_some_and(|label| {
16385            matches!(
16386                node_text(label, source).trim(),
16387                "public" | "private" | "protected"
16388            )
16389        })
16390    {
16391        return prefix.named_children(&mut prefix.walk()).any(|child| {
16392            child.kind() == "declaration"
16393                && child.has_error()
16394                && child
16395                    .named_children(&mut child.walk())
16396                    .any(cpp_reparsed_member_error_is_indexable)
16397        });
16398    }
16399    // A malformed constructor initializer can be split into a declaration
16400    // followed by its compound body when the class prefix already contains
16401    // realistic members. Keep this admission tied to that exact structured
16402    // declaration/error/body chain rather than accepting arbitrary blocks.
16403    prefix.kind() == "declaration"
16404        && prefix.has_error()
16405        && prefix
16406            .named_children(&mut prefix.walk())
16407            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
16408}
16409
16410fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
16411    if !cpp_reparsed_member_error_is_indexable(node) {
16412        return false;
16413    }
16414    let Some(preproc) = node.next_named_sibling() else {
16415        return false;
16416    };
16417    preproc.kind() == "preproc_if"
16418        && preproc.has_error()
16419        && preproc
16420            .named_children(&mut preproc.walk())
16421            .any(|child| child.kind() == "expression_statement" && child.has_error())
16422        && preproc
16423            .next_named_sibling()
16424            .is_some_and(|body| body.kind() == "compound_statement")
16425}
16426
16427/// Return a function body whose braces and ownership are explicit in the
16428/// reparsed class-member tree. An error below a real function envelope is
16429/// recoverable by the ordinary function visitor; a missing/deferred body is
16430/// not, because accepting it would let statement soup masquerade as a member.
16431fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
16432    if node.kind() != "function_definition" {
16433        return None;
16434    }
16435    let body = node.child_by_field_name("body")?;
16436    if body.kind() != "compound_statement" {
16437        return None;
16438    }
16439    let open = body.child(0)?;
16440    let close = body.child(body.child_count().checked_sub(1)?)?;
16441    if open.kind() != "{"
16442        || open.is_missing()
16443        || close.kind() != "}"
16444        || close.is_missing()
16445        || close.end_byte() != body.end_byte()
16446        || body.end_byte() != node.end_byte()
16447    {
16448        return None;
16449    }
16450    Some(body)
16451}
16452
16453fn cpp_reparsed_member_function_errors_are_in_body(
16454    node: Node<'_>,
16455    body: Node<'_>,
16456    source: &str,
16457) -> bool {
16458    let mut cursor = node.walk();
16459    node.children(&mut cursor).all(|child| {
16460        same_node(child, body)
16461            || cpp_reparsed_member_attribute_error(child, source)
16462            || cpp_reparsed_member_signature_identifier_errors(child)
16463            || (!child.has_error() && !child.is_error() && !child.is_missing())
16464    })
16465}
16466
16467/// A complete callable can still carry parser errors in its signature when a
16468/// project annotation is not part of the C++ grammar (`nonneg int`,
16469/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
16470/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
16471/// leaves inside the already-proven callable envelope; structured statements,
16472/// literals, missing tokens, and other malformed signature payload remain
16473/// rejected.
16474fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
16475    if !node.has_error() && !node.is_error() && !node.is_missing() {
16476        return false;
16477    }
16478    let mut stack = vec![node];
16479    let mut saw_error = false;
16480    while let Some(current) = stack.pop() {
16481        if current.is_missing() {
16482            return false;
16483        }
16484        if current.kind() == "ERROR" {
16485            saw_error = true;
16486            let mut cursor = current.walk();
16487            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
16488            if children
16489                .iter()
16490                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
16491            {
16492                return false;
16493            }
16494            stack.extend(children);
16495            continue;
16496        }
16497        let mut cursor = current.walk();
16498        stack.extend(current.children(&mut cursor));
16499    }
16500    saw_error
16501}
16502
16503fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
16504    node.kind() == "ERROR"
16505        && node.named_child_count() == 1
16506        && node.named_child(0).is_some_and(|attribute| {
16507            attribute.kind() == "identifier"
16508                && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(attribute, source)))
16509        })
16510}
16511
16512/// A C++ attribute placed between a member's declarator and body can make
16513/// tree-sitter expose the callable as
16514/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
16515/// Keep this admission tied to that exact node geometry. In particular, an
16516/// arbitrary ERROR or identifier before a compound statement is not enough.
16517fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
16518    let Some(body) = cpp_reparsed_member_function_body(node) else {
16519        return false;
16520    };
16521    let mut cursor = node.walk();
16522    let named = node
16523        .named_children(&mut cursor)
16524        .filter(|child| child.kind() != "comment")
16525        .collect::<Vec<_>>();
16526    let [type_node, error, attribute, body_node] = named.as_slice() else {
16527        return false;
16528    };
16529    if !same_node(*body_node, body)
16530        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16531        || attribute.kind() != "identifier"
16532        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16533        || error.kind() != "ERROR"
16534        || error.named_child_count() != 1
16535    {
16536        return false;
16537    }
16538    error
16539        .named_child(0)
16540        .is_some_and(cpp_reparsed_attribute_callable_declarator)
16541}
16542
16543fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
16544    cpp_structured_type_path(node, source).is_some()
16545        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
16546}
16547
16548fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16549    let Some(body) = cpp_reparsed_member_function_body(node) else {
16550        return false;
16551    };
16552    let mut cursor = node.walk();
16553    let named = node
16554        .named_children(&mut cursor)
16555        .filter(|child| child.kind() != "comment")
16556        .collect::<Vec<_>>();
16557    let [friend, return_error, declarator, body_node] = named.as_slice() else {
16558        return false;
16559    };
16560    let Some(return_type) = return_error.named_child(0) else {
16561        return false;
16562    };
16563    same_node(*body_node, body)
16564        && friend.kind() == "type_identifier"
16565        && node_text(*friend, source) == "friend"
16566        && return_error.kind() == "ERROR"
16567        && return_error.named_child_count() == 1
16568        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16569        && extract_function_declarator(*declarator)
16570            .and_then(cpp_function_declarator_name_node)
16571            .is_some()
16572}
16573
16574fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16575    let Some(body) = cpp_reparsed_member_function_body(node) else {
16576        return false;
16577    };
16578    let mut cursor = node.walk();
16579    let named = node
16580        .named_children(&mut cursor)
16581        .filter(|child| child.kind() != "comment")
16582        .collect::<Vec<_>>();
16583    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
16584        return false;
16585    };
16586    let Some(return_type) = return_error.named_child(0) else {
16587        return false;
16588    };
16589    same_node(*body_node, body)
16590        && prefix
16591            .iter()
16592            .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
16593        && attribute.kind() == "type_identifier"
16594        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16595        && return_error.kind() == "ERROR"
16596        && return_error.named_child_count() == 1
16597        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16598        && extract_function_declarator(*declarator)
16599            .and_then(cpp_function_declarator_name_node)
16600            .is_some()
16601}
16602
16603/// An included-range reparse that begins inside a malformed class can merge an
16604/// access label and following template member. Tree-sitter then emits the label
16605/// as the `template_type` name, the template parameter list as its arguments,
16606/// an ERROR-wrapped return type, the callable declarator, and its complete
16607/// body. Admit only that exact structured displacement.
16608fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
16609    let Some(body) = cpp_reparsed_member_function_body(node) else {
16610        return false;
16611    };
16612    let mut cursor = node.walk();
16613    let named = node
16614        .named_children(&mut cursor)
16615        .filter(|child| child.kind() != "comment")
16616        .collect::<Vec<_>>();
16617    let [template_type, return_error, declarator, body_node] = named.as_slice() else {
16618        return false;
16619    };
16620    let Some(template_name) = template_type.child_by_field_name("name") else {
16621        return false;
16622    };
16623    let Some(arguments) = template_type.child_by_field_name("arguments") else {
16624        return false;
16625    };
16626    let Some(return_type) = return_error.named_child(0) else {
16627        return false;
16628    };
16629    let mut cursor = template_type.walk();
16630    let template_errors = template_type
16631        .named_children(&mut cursor)
16632        .filter(|child| child.kind() == "ERROR")
16633        .collect::<Vec<_>>();
16634    let [comment_error] = template_errors.as_slice() else {
16635        return false;
16636    };
16637    let mut cursor = comment_error.walk();
16638    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
16639    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
16640        return false;
16641    };
16642    same_node(*body_node, body)
16643        && template_type.kind() == "template_type"
16644        && template_name.kind() == "type_identifier"
16645        && matches!(
16646            node_text(template_name, source).trim(),
16647            "public" | "private" | "protected"
16648        )
16649        && arguments.kind() == "template_argument_list"
16650        && arguments.named_child_count() > 0
16651        && !arguments.has_error()
16652        && !colon.is_named()
16653        && colon.kind() == ":"
16654        && comments.iter().all(|child| child.kind() == "comment")
16655        && !template_keyword.is_named()
16656        && template_keyword.kind() == "template"
16657        && return_error.kind() == "ERROR"
16658        && return_error.named_child_count() == 1
16659        && cpp_reparsed_member_return_type_is_indexable(return_type, source)
16660        && extract_function_declarator(*declarator)
16661            .and_then(cpp_function_declarator_name_node)
16662            .is_some()
16663}
16664
16665/// Return the constructor declaration tree-sitter can merge into an access
16666/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
16667/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
16668/// declaration's apparent type; the callable name must still exactly match the
16669/// recovered class, so unrelated labeled statements are never re-owned.
16670fn cpp_reparsed_preprocessor_constructor<'tree>(
16671    node: Node<'tree>,
16672    class_name: &str,
16673    source: &str,
16674) -> Option<Node<'tree>> {
16675    if node.kind() != "labeled_statement" {
16676        return None;
16677    }
16678    let mut cursor = node.walk();
16679    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16680    let [label, directive_error, declaration] = named.as_slice() else {
16681        return None;
16682    };
16683    if label.kind() != "statement_identifier"
16684        || !matches!(
16685            node_text(*label, source),
16686            "public" | "private" | "protected"
16687        )
16688        || directive_error.kind() != "ERROR"
16689        || directive_error.child_count() != 1
16690        || directive_error
16691            .child(0)
16692            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
16693        || declaration.kind() != "declaration"
16694        || declaration.named_child_count() != 2
16695    {
16696        return None;
16697    }
16698    let apparent_type = declaration.child_by_field_name("type")?;
16699    if apparent_type.kind() != "type_identifier"
16700        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
16701    {
16702        return None;
16703    }
16704    let declarator = declaration.child_by_field_name("declarator")?;
16705    let function = extract_function_declarator(declarator)?;
16706    let name = cpp_function_declarator_name_node(function)?;
16707    (node_text(name, source) == class_name).then_some(*declaration)
16708}
16709
16710fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
16711    if extract_function_declarator(node)
16712        .and_then(cpp_function_declarator_name_node)
16713        .is_some()
16714    {
16715        return true;
16716    }
16717    node.kind() == "init_declarator"
16718        && node
16719            .child_by_field_name("declarator")
16720            .is_some_and(|declarator| declarator.kind() == "identifier")
16721        && node
16722            .child_by_field_name("value")
16723            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
16724}
16725
16726/// Return true for the constrained/attribute form that tree-sitter splits into
16727/// an ERROR declaration, a preprocessor `requires` clause, and a following
16728/// compound statement. The three nodes must remain immediate named siblings;
16729/// this deliberately does not search source text or skip unrelated statements.
16730fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
16731    if node.kind() != "ERROR" || node.named_child_count() != 3 {
16732        return false;
16733    }
16734    let mut cursor = node.walk();
16735    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16736    let [type_node, function_declarator, attribute] = named.as_slice() else {
16737        return false;
16738    };
16739    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
16740        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
16741        || attribute.kind() != "identifier"
16742        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
16743    {
16744        return false;
16745    }
16746    let Some(preproc) =
16747        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16748    else {
16749        return false;
16750    };
16751    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
16752        .filter(|sibling| sibling.kind() == "compound_statement")
16753    else {
16754        return false;
16755    };
16756    let Some(open) = body.child(0) else {
16757        return false;
16758    };
16759    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
16760        return false;
16761    };
16762    let Some(condition) = preproc.child_by_field_name("condition") else {
16763        return false;
16764    };
16765    let mut cursor = preproc.walk();
16766    let payload = preproc
16767        .named_children(&mut cursor)
16768        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
16769        .collect::<Vec<_>>();
16770    let [requires_statement] = payload.as_slice() else {
16771        return false;
16772    };
16773    let requires_clause = requires_statement.named_child(0);
16774
16775    open.kind() == "{"
16776        && !open.is_missing()
16777        && close.kind() == "}"
16778        && !close.is_missing()
16779        && close.end_byte() == body.end_byte()
16780        && requires_statement.kind() == "expression_statement"
16781        && requires_statement.named_child_count() == 1
16782        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
16783}
16784
16785fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16786    let mut sibling = node.next_named_sibling();
16787    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16788        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
16789    }
16790    sibling
16791}
16792
16793fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
16794    let mut sibling = node.prev_named_sibling();
16795    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
16796        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
16797    }
16798    sibling
16799}
16800
16801fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
16802    let Some(preproc) =
16803        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
16804    else {
16805        return false;
16806    };
16807    let Some(error) =
16808        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
16809    else {
16810        return false;
16811    };
16812    cpp_reparsed_attribute_requires_error(error, source)
16813}
16814
16815fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
16816    node: Node<'tree>,
16817    source: &str,
16818) -> Option<Node<'tree>> {
16819    if node.kind() != "ERROR" {
16820        return None;
16821    }
16822    let mut cursor = node.walk();
16823    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16824    let [parameter, macro_name, message] = named.as_slice() else {
16825        return None;
16826    };
16827    let parameter_name = parameter.named_child(0)?;
16828    (parameter.kind() == "type_parameter_declaration"
16829        && parameter_name.kind() == "type_identifier"
16830        && macro_name.kind() == "type_identifier"
16831        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16832        && message.kind() == "string_literal")
16833        .then_some(parameter_name)
16834}
16835
16836/// Recognize the alternate constraint-macro prefix where tree-sitter retains
16837/// the complete qualified constraint as a fourth child instead of moving it
16838/// into the following function. Keep the gate tied to a two-type template
16839/// constraint that names the declared type parameter.
16840fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
16841    node: Node<'tree>,
16842    source: &str,
16843) -> Option<Node<'tree>> {
16844    if node.kind() != "ERROR" {
16845        return None;
16846    }
16847    let mut cursor = node.walk();
16848    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
16849    let [parameter, macro_name, message, constraint] = named.as_slice() else {
16850        return None;
16851    };
16852    let parameter_name = parameter.named_child(0)?;
16853    let constraint_scope = constraint.child_by_field_name("scope")?;
16854    let constraint_template = constraint.child_by_field_name("name")?;
16855    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16856    let mut argument_cursor = constraint_arguments.walk();
16857    let constraint_types = constraint_arguments
16858        .named_children(&mut argument_cursor)
16859        .collect::<Vec<_>>();
16860    if parameter.kind() != "type_parameter_declaration"
16861        || parameter_name.kind() != "type_identifier"
16862        || macro_name.kind() != "type_identifier"
16863        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*macro_name, source)))
16864        || message.kind() != "string_literal"
16865        || constraint.kind() != "qualified_identifier"
16866        || constraint_scope.kind() != "namespace_identifier"
16867        || !matches!(
16868            constraint_template.kind(),
16869            "template_function" | "template_type"
16870        )
16871        || !matches!(constraint_types.as_slice(), [left, right]
16872            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16873        || constraint_arguments.has_error()
16874    {
16875        return None;
16876    }
16877    let parameter_text = node_text(parameter_name, source);
16878    let mut stack = constraint_types;
16879    while let Some(current) = stack.pop() {
16880        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
16881            return Some(parameter_name);
16882        }
16883        let mut cursor = current.walk();
16884        stack.extend(current.named_children(&mut cursor));
16885    }
16886    None
16887}
16888
16889fn cpp_reparsed_template_macro_companion_is_indexable(
16890    node: Node<'_>,
16891    parameter_name: Node<'_>,
16892    source: &str,
16893) -> bool {
16894    let Some(body) = cpp_reparsed_member_function_body(node) else {
16895        return false;
16896    };
16897    let mut cursor = node.walk();
16898    let named = node
16899        .named_children(&mut cursor)
16900        .filter(|child| child.kind() != "comment")
16901        .collect::<Vec<_>>();
16902    let [
16903        constraint,
16904        close_error,
16905        storage,
16906        return_error,
16907        declarator,
16908        body_node,
16909    ] = named.as_slice()
16910    else {
16911        return false;
16912    };
16913    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
16914        return false;
16915    };
16916    let Some(constraint_template) = constraint.child_by_field_name("name") else {
16917        return false;
16918    };
16919    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
16920        return false;
16921    };
16922    let Some(return_type) = return_error.named_child(0) else {
16923        return false;
16924    };
16925    let mut cursor = constraint_arguments.walk();
16926    let constraint_types = constraint_arguments
16927        .named_children(&mut cursor)
16928        .collect::<Vec<_>>();
16929    same_node(*body_node, body)
16930        && constraint.kind() == "qualified_identifier"
16931        && constraint_scope.kind() == "namespace_identifier"
16932        && constraint_template.kind() == "template_type"
16933        && matches!(constraint_types.as_slice(), [left, right]
16934            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16935        && !constraint_arguments.has_error()
16936        && close_error.kind() == "ERROR"
16937        && close_error.named_child_count() == 0
16938        && storage.kind() == "storage_class_specifier"
16939        && return_error.kind() == "ERROR"
16940        && return_error.named_child_count() == 1
16941        && return_type.kind() == "identifier"
16942        && node_text(return_type, source) == node_text(parameter_name, source)
16943        && extract_function_declarator(*declarator)
16944            .and_then(cpp_function_declarator_name_node)
16945            .is_some()
16946}
16947
16948fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
16949    node: Node<'tree>,
16950    parameter_name: Node<'_>,
16951    source: &str,
16952) -> Option<Node<'tree>> {
16953    let body = cpp_reparsed_member_function_body(node)?;
16954    let constraint = node.child_by_field_name("type")?;
16955    let constraint_template = constraint.child_by_field_name("name")?;
16956    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
16957    let mut argument_cursor = constraint_arguments.walk();
16958    let constraint_types = constraint_arguments
16959        .named_children(&mut argument_cursor)
16960        .collect::<Vec<_>>();
16961    if constraint.kind() != "qualified_identifier"
16962        || constraint_template.kind() != "template_type"
16963        || !matches!(constraint_types.as_slice(), [left, right]
16964            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
16965        || constraint_arguments.has_error()
16966        || node
16967            .child_by_field_name("body")
16968            .is_none_or(|candidate| !same_node(candidate, body))
16969    {
16970        return None;
16971    }
16972
16973    let mut cursor = node.walk();
16974    let recovery_errors = node
16975        .named_children(&mut cursor)
16976        .filter(|child| child.kind() == "ERROR")
16977        .collect::<Vec<_>>();
16978    if !recovery_errors
16979        .iter()
16980        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
16981        || !recovery_errors.iter().all(|error| {
16982            error.named_child_count() == 0
16983                || cpp_reparsed_constraint_macro_error(*error, source)
16984                || (error.named_child_count() == 1
16985                    && error
16986                        .named_child(0)
16987                        .is_some_and(|child| child.kind() == "function_declarator"))
16988        })
16989    {
16990        return None;
16991    }
16992
16993    let parameter_text = node_text(parameter_name, source);
16994    let mut declarators = node
16995        .child_by_field_name("declarator")
16996        .and_then(extract_function_declarator)
16997        .into_iter()
16998        .collect::<Vec<_>>();
16999    for error in recovery_errors {
17000        let mut stack = vec![error];
17001        while let Some(current) = stack.pop() {
17002            if current.kind() == "function_declarator" {
17003                declarators.push(current);
17004            }
17005            let mut cursor = current.walk();
17006            stack.extend(current.named_children(&mut cursor));
17007        }
17008    }
17009    declarators.into_iter().find(|declarator| {
17010        cpp_function_declarator_name_node(*declarator)
17011            .is_some_and(|name| name.kind() == "identifier")
17012            && declarator
17013                .child_by_field_name("parameters")
17014                .is_some_and(|parameters| {
17015                    parameters
17016                        .named_children(&mut parameters.walk())
17017                        .filter_map(|parameter| parameter.child_by_field_name("type"))
17018                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
17019                })
17020    })
17021}
17022
17023fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
17024    node: Node<'_>,
17025    parameter_name: Node<'_>,
17026    source: &str,
17027) -> bool {
17028    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
17029}
17030
17031fn cpp_reparsed_template_macro_function_companion_is_indexable(
17032    node: Node<'_>,
17033    parameter_name: Node<'_>,
17034    source: &str,
17035) -> bool {
17036    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
17037        return false;
17038    }
17039    let Some(return_type) = node.child_by_field_name("type") else {
17040        return false;
17041    };
17042    let Some(function_declarator) = node
17043        .child_by_field_name("declarator")
17044        .and_then(extract_function_declarator)
17045    else {
17046        return false;
17047    };
17048    if cpp_function_declarator_name_node(function_declarator).is_none()
17049        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
17050    {
17051        return false;
17052    }
17053    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
17054        return false;
17055    };
17056    let parameter_text = node_text(parameter_name, source);
17057    parameters
17058        .named_children(&mut parameters.walk())
17059        .any(|parameter| {
17060            parameter
17061                .child_by_field_name("type")
17062                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
17063        })
17064}
17065
17066fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
17067    if node.kind() != "ERROR" {
17068        return false;
17069    }
17070    let mut stack = vec![node];
17071    while let Some(current) = stack.pop() {
17072        let macro_shape = match current.kind() {
17073            "call_expression" => current
17074                .child_by_field_name("function")
17075                .zip(current.child_by_field_name("arguments")),
17076            "init_declarator" => current
17077                .child_by_field_name("declarator")
17078                .zip(current.child_by_field_name("value")),
17079            _ => None,
17080        };
17081        if let Some((name, arguments)) = macro_shape
17082            && name.kind() == "identifier"
17083            && arguments.kind() == "argument_list"
17084            && arguments.named_child_count() >= 2
17085            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
17086        {
17087            return true;
17088        }
17089        let mut cursor = current.walk();
17090        stack.extend(current.named_children(&mut cursor));
17091    }
17092    false
17093}
17094
17095fn cpp_recovered_template_macro_constructor<'tree>(
17096    node: Node<'tree>,
17097    source: &str,
17098) -> Option<(Node<'tree>, Node<'tree>)> {
17099    let mut prefix = node.prev_named_sibling()?;
17100    while prefix.kind() == "comment" {
17101        prefix = prefix.prev_named_sibling()?;
17102    }
17103    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
17104    let parameter = parameter_name
17105        .parent()
17106        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
17107    let declarator =
17108        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
17109    Some((declarator, parameter))
17110}
17111
17112fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
17113    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
17114        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
17115            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
17116                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
17117                    function,
17118                    parameter_name,
17119                    source,
17120                )
17121        });
17122    }
17123    let Some(parameter_name) =
17124        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
17125    else {
17126        return false;
17127    };
17128    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
17129        cpp_reparsed_template_macro_function_companion_is_indexable(
17130            function,
17131            parameter_name,
17132            source,
17133        )
17134    })
17135}
17136
17137fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
17138    let function_name = node
17139        .child_by_field_name("declarator")
17140        .and_then(extract_function_declarator)
17141        .and_then(cpp_function_declarator_name_node);
17142    if let Some(body) = cpp_reparsed_member_function_body(node)
17143        && function_name.is_some()
17144        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
17145    {
17146        return true;
17147    }
17148    cpp_reparsed_attribute_member_function(node, source)
17149        || cpp_reparsed_friend_function_is_indexable(node, source)
17150        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
17151        || cpp_reparsed_access_template_function_is_indexable(node, source)
17152        || cpp_recovered_template_macro_constructor(node, source).is_some()
17153}
17154
17155/// Recognize the three top-level nodes produced when an unknown attribute
17156/// macro separates an inline member's declarator from its body in a reparsed
17157/// class interior: an errorful declaration with a missing semicolon, the macro
17158/// call expression, and the complete compound body. Their adjacency and exact
17159/// structured shapes prove one recoverable member envelope; arbitrary calls or
17160/// blocks do not pass this gate.
17161fn cpp_reparsed_macro_attribute_member_sequence(
17162    children: &[Node<'_>],
17163    index: usize,
17164    source: &str,
17165) -> bool {
17166    let Some(prefix) = children.get(index).copied() else {
17167        return false;
17168    };
17169    let declaration = if prefix.kind() == "labeled_statement" {
17170        prefix
17171            .named_child(prefix.named_child_count().saturating_sub(1))
17172            .filter(|child| child.kind() == "declaration")
17173    } else {
17174        (prefix.kind() == "declaration").then_some(prefix)
17175    };
17176    let Some(declaration) = declaration else {
17177        return false;
17178    };
17179    if !declaration.has_error()
17180        || declaration
17181            .child_by_field_name("declarator")
17182            .and_then(extract_function_declarator)
17183            .and_then(cpp_function_declarator_name_node)
17184            .is_none()
17185    {
17186        return false;
17187    }
17188    let Some(attribute_statement) = children.get(index + 1).copied() else {
17189        return false;
17190    };
17191    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
17192        .then(|| attribute_statement.named_child(0))
17193        .flatten()
17194        .filter(|child| child.kind() == "call_expression")
17195    else {
17196        return false;
17197    };
17198    let Some(attribute_name) = attribute_call
17199        .child_by_field_name("function")
17200        .filter(|function| function.kind() == "identifier")
17201        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
17202    else {
17203        return false;
17204    };
17205    if !cpp_export_macro_token(&attribute_name) {
17206        return false;
17207    }
17208    let Some(body) = children.get(index + 2).copied() else {
17209        return false;
17210    };
17211    body.kind() == "compound_statement"
17212        && body.child(0).is_some_and(|open| open.kind() == "{")
17213        && body
17214            .child(body.child_count().saturating_sub(1))
17215            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
17216        && declaration.end_byte() <= attribute_statement.start_byte()
17217        && attribute_statement.end_byte() <= body.start_byte()
17218}
17219
17220/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
17221/// types and specifiers followed by a declarator, over and over, with nothing
17222/// left over. A string-argument attribute macro is what strands them there, and
17223/// the walk indexes exactly what this reads, so admitting the region is safe
17224/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
17225/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
17226/// the whole class body.
17227fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
17228    if node.kind() != "ERROR" {
17229        return false;
17230    }
17231    let run = stranded_declaration_run(node, source);
17232    run.complete && !run.declarations.is_empty()
17233}
17234
17235fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
17236    let mut cursor = root.walk();
17237    let children = root.named_children(&mut cursor).collect::<Vec<_>>();
17238    let mut saw_member = false;
17239    let mut index = 0;
17240    while index < children.len() {
17241        let child = children[index];
17242        if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
17243            saw_member = true;
17244            index += 3;
17245            continue;
17246        }
17247        if let Some(recovered) = fragmented_class_body(child, source) {
17248            let Some(tree) = cpp_reparse_fragmented_class_body(
17249                source,
17250                recovered.body.reparse_start,
17251                recovered.body.reparse_end,
17252            ) else {
17253                return false;
17254            };
17255            if !cpp_reparsed_members_are_indexable(tree.root_node(), source) {
17256                return false;
17257            }
17258            saw_member = true;
17259            index += 1;
17260            while index < children.len()
17261                && children[index].end_byte() <= recovered.body.class_range.end_byte
17262            {
17263                index += 1;
17264            }
17265            continue;
17266        }
17267        match child.kind() {
17268            "comment" => {}
17269            "labeled_statement" => saw_member = true,
17270            "function_definition" => {
17271                if child.has_error()
17272                    && !cpp_reparsed_member_function_is_indexable(child, source)
17273                    && cpp_sentinel_macro_region(child, source).is_none()
17274                {
17275                    return false;
17276                }
17277                saw_member = true;
17278            }
17279            // The attribute a string-argument macro leaves as a call statement
17280            // of its own. It declares nothing; the member it decorated is the
17281            // sibling after it, checked on its own turn.
17282            "expression_statement" if is_string_attribute_macro_statement(child) => {}
17283            "ERROR"
17284                if (cpp_reparsed_member_error_is_indexable(child)
17285                    || cpp_reparsed_adjacent_copy_control_error(child, source)
17286                    || cpp_reparsed_stranded_member_error(child, source))
17287                    && (child
17288                        .next_named_sibling()
17289                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
17290                        || cpp_reparsed_member_error_with_preprocessed_body(child)) =>
17291            {
17292                saw_member = true;
17293            }
17294            "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
17295                saw_member = true;
17296            }
17297            "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
17298                saw_member = true;
17299            }
17300            "expression_statement"
17301                if cpp_is_stray_semicolon(child, source)
17302                    && child.prev_named_sibling().is_some_and(|error| {
17303                        cpp_reparsed_member_error_is_indexable(error)
17304                            || cpp_reparsed_adjacent_copy_control_error(error, source)
17305                            || cpp_reparsed_stranded_member_error(error, source)
17306                    }) =>
17307            {
17308                saw_member = true;
17309            }
17310            "compound_statement"
17311                if cpp_reparsed_constructor_body_is_indexable(child, source)
17312                    || cpp_reparsed_attribute_requires_body(child, source) =>
17313            {
17314                saw_member = true;
17315            }
17316            kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
17317            _ => return false,
17318        }
17319        index += 1;
17320    }
17321    saw_member
17322}
17323
17324/// Detect the malformed constructor shape that tree-sitter exposes as an
17325/// access-label statement followed by initializer-looking declarations. The
17326/// declarations are not class members: visiting their `location(loc)` and
17327/// `string(s)` function declarators would publish synthetic functions. The
17328/// export-class fallback keeps the original sibling nodes and therefore avoids
17329/// this parser artifact. The returned range identifies the real constructor
17330/// header, which can be reparsed independently as a structured declarator.
17331fn cpp_reparsed_synthetic_initializer_constructor_range(
17332    root: Node<'_>,
17333    class_name: &str,
17334    source: &str,
17335    constructor_end: usize,
17336) -> Option<std::ops::Range<usize>> {
17337    let mut stack = {
17338        let mut cursor = root.walk();
17339        root.named_children(&mut cursor).collect::<Vec<_>>()
17340    };
17341    while let Some(current) = stack.pop() {
17342        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
17343            current,
17344            class_name,
17345            source,
17346            constructor_end,
17347        ) {
17348            return Some(range);
17349        }
17350        if current.kind() == "ERROR" {
17351            let mut cursor = current.walk();
17352            stack.extend(current.named_children(&mut cursor));
17353        }
17354    }
17355    None
17356}
17357
17358/// Recover an inline constructor that a function-like export macro makes
17359/// tree-sitter merge with the following overload. In the reparsed class-body
17360/// region, the access label wraps one declaration whose ERROR contains the
17361/// constructor declarator and its base-initializer/body, while the declaration's
17362/// ordinary declarator is the following overload. Every boundary below comes
17363/// from that CST; no source syntax is reparsed by hand.
17364fn cpp_reparsed_merged_inline_constructor<'tree>(
17365    root: Node<'tree>,
17366    class_name: &str,
17367    source: &str,
17368) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
17369    let mut stack = vec![root];
17370    while let Some(current) = stack.pop() {
17371        if current.kind() != "labeled_statement" {
17372            let mut cursor = current.walk();
17373            stack.extend(current.named_children(&mut cursor));
17374            continue;
17375        }
17376        let declaration = current
17377            .named_children(&mut current.walk())
17378            .find(|child| child.kind() == "declaration")?;
17379        if declaration
17380            .child_by_field_name("type")
17381            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
17382        {
17383            continue;
17384        }
17385        let following = declaration
17386            .child_by_field_name("declarator")
17387            .and_then(extract_function_declarator)
17388            .and_then(cpp_function_declarator_name_node);
17389        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
17390            continue;
17391        }
17392        let mut declaration_cursor = declaration.walk();
17393        let Some(error) = declaration
17394            .named_children(&mut declaration_cursor)
17395            .find(|child| child.kind() == "ERROR")
17396        else {
17397            continue;
17398        };
17399        let mut error_cursor = error.walk();
17400        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
17401        let Some(constructor) = error_children.iter().copied().find(|child| {
17402            child.kind() == "function_declarator"
17403                && cpp_function_declarator_name_node(*child)
17404                    .is_some_and(|name| node_text(name, source).trim() == class_name)
17405        }) else {
17406            continue;
17407        };
17408        let Some(body) = error_children.iter().copied().find_map(|child| {
17409            (child.kind() == "init_declarator")
17410                .then(|| child.child_by_field_name("value"))
17411                .flatten()
17412                .filter(|value| value.kind() == "initializer_list")
17413        }) else {
17414            continue;
17415        };
17416        if constructor.end_byte() > body.start_byte() {
17417            continue;
17418        }
17419        return Some((constructor.start_byte()..body.end_byte(), body));
17420    }
17421    None
17422}
17423
17424fn cpp_reparsed_synthetic_initializer_constructor(
17425    node: Node<'_>,
17426    class_name: &str,
17427    source: &str,
17428    constructor_end: usize,
17429) -> Option<std::ops::Range<usize>> {
17430    if node.kind() != "labeled_statement" {
17431        return None;
17432    }
17433    let mut cursor = node.walk();
17434    let named = node
17435        .named_children(&mut cursor)
17436        .filter(|child| child.kind() != "comment")
17437        .collect::<Vec<_>>();
17438    let label = named.first()?;
17439    if label.kind() != "statement_identifier"
17440        || !matches!(
17441            node_text(*label, source).trim(),
17442            "public" | "private" | "protected"
17443        )
17444    {
17445        return None;
17446    }
17447    let call_error_index = named.iter().position(|child| {
17448        if child.kind() != "ERROR" {
17449            return false;
17450        }
17451        let mut stack = vec![*child];
17452        while let Some(current) = stack.pop() {
17453            if current.kind() == "call_expression"
17454                && current
17455                    .child_by_field_name("function")
17456                    .is_some_and(|function| {
17457                        function.kind() == "identifier"
17458                            && node_text(function, source).trim() == class_name
17459                    })
17460            {
17461                return true;
17462            }
17463            let mut cursor = current.walk();
17464            stack.extend(current.named_children(&mut cursor));
17465        }
17466        false
17467    })?;
17468    let constructor_call = {
17469        let mut stack = vec![named[call_error_index]];
17470        let mut found = None;
17471        while let Some(current) = stack.pop() {
17472            if current.kind() == "call_expression"
17473                && current
17474                    .child_by_field_name("function")
17475                    .is_some_and(|function| {
17476                        function.kind() == "identifier"
17477                            && node_text(function, source).trim() == class_name
17478                    })
17479            {
17480                found = Some(current);
17481                break;
17482            }
17483            let mut cursor = current.walk();
17484            stack.extend(current.named_children(&mut cursor));
17485        }
17486        found
17487    };
17488    let constructor_call = constructor_call?;
17489    named.iter().skip(call_error_index + 1).find(|child| {
17490        child.kind() == "declaration" && child.has_error() && {
17491            let mut cursor = child.walk();
17492            child.named_children(&mut cursor).any(|declarator| {
17493                declarator.kind() == "init_declarator"
17494                    && declarator
17495                        .child_by_field_name("declarator")
17496                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
17497                    && declarator
17498                        .child_by_field_name("value")
17499                        .is_some_and(|value| value.kind() == "initializer_list")
17500            })
17501        }
17502    })?;
17503    Some(constructor_call.start_byte()..constructor_end)
17504}
17505
17506fn cpp_reparsed_exact_constructor_declarator<'tree>(
17507    root: Node<'tree>,
17508    start: usize,
17509    class_name: &str,
17510    source: &str,
17511) -> Option<Node<'tree>> {
17512    let mut candidate = None;
17513    let mut stack = vec![root];
17514    while let Some(current) = stack.pop() {
17515        if current.kind() == "function_declarator"
17516            && current.start_byte() == start
17517            && cpp_function_declarator_name_node(current)
17518                .is_some_and(|name| node_text(name, source).trim() == class_name)
17519        {
17520            if candidate.is_some() {
17521                return None;
17522            }
17523            candidate = Some(current);
17524            continue;
17525        }
17526        let mut cursor = current.walk();
17527        stack.extend(current.named_children(&mut cursor));
17528    }
17529    candidate
17530}
17531
17532fn cpp_is_indexable_item_kind(kind: &str) -> bool {
17533    matches!(
17534        kind,
17535        "namespace_definition"
17536            | "class_specifier"
17537            | "struct_specifier"
17538            | "union_specifier"
17539            | "enum_specifier"
17540            | "function_definition"
17541            | "template_declaration"
17542            | "declaration"
17543            | "field_declaration"
17544            | "alias_declaration"
17545            | "static_assert_declaration"
17546            | "type_definition"
17547            | "using_declaration"
17548            | "linkage_specification"
17549            | "preproc_def"
17550            | "preproc_function_def"
17551            | "preproc_include"
17552            | "preproc_if"
17553            | "preproc_ifdef"
17554            | "preproc_call"
17555    )
17556}
17557
17558#[cfg(test)]
17559mod tests {
17560    use super::*;
17561    use crate::adapter::parse_cpp_file;
17562    use brokk_bifrost_core::analyzer::parsed_file::{
17563        finish_code_unit_removal_scan_probe, finish_declaration_identity_comparison_probe,
17564        start_code_unit_removal_scan_probe, start_declaration_identity_comparison_probe,
17565    };
17566    use std::fmt::Write;
17567
17568    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
17569        let mut parser = tree_sitter::Parser::new();
17570        parser
17571            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17572            .unwrap();
17573        let tree = parser.parse(source, None).unwrap();
17574        let file = ProjectFile::new(std::env::temp_dir(), name);
17575        parse_cpp_file(&file, source, &tree)
17576    }
17577
17578    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
17579    /// exactly the spelling it was given, with the nested-class `$` recorded as
17580    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
17581    #[test]
17582    fn rendered_name_fq_round_trips_nested_class_spellings() {
17583        assert!(cpp_rendered_name_fq("").is_empty());
17584        for rendered in [
17585            "Only",
17586            "Top$Only",
17587            "ns.Outer$Inner",
17588            "Top$Only.b",
17589            "ns.Outer$Inner.a",
17590            "Buck$Stop",
17591        ] {
17592            let fq = cpp_rendered_name_fq(rendered);
17593            assert_eq!(
17594                fq.display_native(Language::Cpp, segment_interner()),
17595                rendered,
17596                "native round trip for {rendered}"
17597            );
17598        }
17599        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
17600            .segments()
17601            .iter()
17602            .map(|segment| segment_interner().resolve(*segment).1)
17603            .collect();
17604        assert_eq!(
17605            kinds,
17606            [
17607                SegmentKind::Unknown,
17608                SegmentKind::Unknown,
17609                SegmentKind::Nested
17610            ]
17611        );
17612    }
17613
17614    /// #3631: a `::`-only source spelling must name the nested `$` identity
17615    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
17616    /// the single rendered-name parse.
17617    #[test]
17618    fn colon_source_aliases_name_nested_and_namespace_identities() {
17619        let native = |rendered: &str| {
17620            cpp_colon_source_aliases(rendered)
17621                .into_iter()
17622                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
17623                .collect::<Vec<_>>()
17624        };
17625        let top = native("Top::Only");
17626        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
17627        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
17628        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
17629        let nested = native("ns::Outer::Inner");
17630        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
17631        assert!(
17632            nested.contains(&"ns::Outer.Inner".to_string()),
17633            "{nested:?}"
17634        );
17635        let member = native("Top::Only::b");
17636        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
17637        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
17638        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
17639        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
17640        assert!(cpp_colon_source_aliases("Only").is_empty());
17641        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
17642            fq.segments()
17643                .iter()
17644                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
17645        }));
17646    }
17647
17648    #[test]
17649    fn displaced_terminator_ignores_nested_initializer_endif() {
17650        let source = "#ifdef ENABLE_ITEMS\n\
17651struct Item { int reg; };\n\
17652static const struct Item items[] = {{1}};\n\
17653static const struct Item extras[] = {\n\
17654  {0},\n\
17655#ifndef REDUCED\n\
17656  {1},\n\
17657#endif\n\
17658};\n\
17659int read_item(int i) { return items[i].reg; }\n\
17660#endif\n";
17661        let mut parser = tree_sitter::Parser::new();
17662        parser
17663            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17664            .expect("C++ grammar");
17665        let tree = parser.parse(source, None).expect("fixture tree");
17666        let conditional = tree
17667            .root_node()
17668            .named_child(0)
17669            .filter(|node| node.kind() == "preproc_ifdef")
17670            .expect("outer conditional");
17671
17672        assert!(conditional.has_error());
17673        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
17674        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
17675    }
17676
17677    #[test]
17678    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
17679        let source = "struct Image { PyObject_HEAD Imaging image; };";
17680        let parsed = parse_cpp_declarations(source, "image.h");
17681        let names = parsed
17682            .declarations()
17683            .iter()
17684            .map(|unit| unit.fq_name())
17685            .collect::<Vec<_>>();
17686
17687        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
17688        assert!(
17689            names.iter().all(|name| name != "Image.Imaging"),
17690            "the pseudo-declarator must not become a field: {names:#?}"
17691        );
17692
17693        let pointer = parse_cpp_declarations(
17694            "struct Image { PyObject_HEAD Imaging *image; };",
17695            "image-pointer.h",
17696        );
17697        let pointer_names = pointer
17698            .declarations()
17699            .iter()
17700            .map(|unit| unit.fq_name())
17701            .collect::<Vec<_>>();
17702        assert!(
17703            pointer_names.iter().any(|name| name == "Image.image"),
17704            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
17705        );
17706        assert!(
17707            pointer_names.iter().all(|name| name != "Image.Imaging"),
17708            "the pointer recovery error must not become a field: {pointer_names:#?}"
17709        );
17710
17711        let unrelated_macro = parse_cpp_declarations(
17712            "struct Image { OTHER_HEAD Imaging other; };",
17713            "image-near-miss.h",
17714        );
17715        let unrelated_names = unrelated_macro
17716            .declarations()
17717            .iter()
17718            .map(|unit| unit.fq_name())
17719            .collect::<Vec<_>>();
17720        assert!(
17721            unrelated_names.iter().all(|name| name != "Image.other"),
17722            "an unrelated macro with the same malformed CST shape must fail closed: {unrelated_names:#?}"
17723        );
17724    }
17725
17726    #[test]
17727    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
17728        let source = r#"namespace testing {
17729namespace internal {
17730    namespace detail {
17731        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
17732        public:
17733            int value() const { return count_ + 1; }
17734        private:
17735            int count_;
17736        };
17737        class GTEST_API_ [[nodiscard]] OtherReporter {
17738        public:
17739            int value() const { return count_ + 2; }
17740        private:
17741            int count_;
17742        };
17743    }
17744
17745    template <typename T>
17746    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
17747
17748    class TailReporter {};
17749}
17750}
17751"#;
17752        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
17753        let declarations = parsed.declarations();
17754        let mut parser = tree_sitter::Parser::new();
17755        parser
17756            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17757            .unwrap();
17758        let tree = parser.parse(source, None).unwrap();
17759        let tail_start = source.find("TailReporter").expect("tail class");
17760        let tail_node = tree
17761            .root_node()
17762            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
17763            .expect("tail class AST node");
17764        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
17765        assert!(
17766            tree.root_node().has_error(),
17767            "the malformed class must exercise recovery"
17768        );
17769        assert!(index.region_at(tail_start).is_some());
17770        assert_eq!(
17771            index.enclosing_namespace_components(tail_node, source),
17772            ["testing", "internal"]
17773        );
17774        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
17775        let mut recovered_parsed = ParsedFile::new(String::new());
17776        let class_unit = CodeUnit::new_fq(
17777            file.clone(),
17778            CodeUnitType::Class,
17779            "testing",
17780            "ScopedFakeTestPartResultReporter",
17781            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
17782        );
17783        let scope = ScopeInfo {
17784            package_name: "testing".to_string(),
17785            module: None,
17786            class_unit: Some(class_unit),
17787            template_signature: Some("<typename T>".to_string()),
17788            template_metadata: Some(CppTemplateMetadata {
17789                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
17790                primary_fq_name: String::new(),
17791                parameters: Vec::new(),
17792                specialization_arguments: Vec::new(),
17793                alias_target: None,
17794            }),
17795            declarations_are_fields: true,
17796            recovered_specialization_member_scope: true,
17797            visible_using_namespaces: Vec::new(),
17798        };
17799        let mut visitor = CppVisitor {
17800            file: &file,
17801            source,
17802            parsed: &mut recovered_parsed,
17803            c_tag_semantics: false,
17804            c_tag_scope_witness: false,
17805            recovered_class_sibling_scopes: HashMap::default(),
17806            consumed_fragment_regions: Vec::new(),
17807            orphaned_namespaces: &index,
17808            partitioned_regions: Vec::new(),
17809            namespace_forward_scans: HashMap::default(),
17810            field_owners: None,
17811            recovery_captures: Vec::new(),
17812            object_macro_fields: HashMap::default(),
17813            ambiguous_object_macro_fields: HashSet::default(),
17814        };
17815        let recovered = visitor
17816            .recovered_namespace_scope(tail_node, &scope)
17817            .expect("the tail must use the stolen namespace scope");
17818        assert_eq!(recovered.package_name, "testing::internal");
17819        assert!(
17820            recovered.class_unit.is_none(),
17821            "recovered namespace declarations cannot retain the malformed class owner"
17822        );
17823        assert!(recovered.template_signature.is_none());
17824        assert!(recovered.template_metadata.is_none());
17825        assert!(!recovered.declarations_are_fields);
17826        assert!(!recovered.recovered_specialization_member_scope);
17827        assert!(
17828            declarations
17829                .iter()
17830                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
17831            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
17832        );
17833        assert!(
17834            declarations
17835                .iter()
17836                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
17837            "the recovered free function remains in its namespace: {declarations:#?}"
17838        );
17839        assert!(
17840            declarations
17841                .iter()
17842                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
17843            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
17844        );
17845        assert!(
17846            declarations.iter().all(|unit| {
17847                !unit
17848                    .short_name()
17849                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
17850            }),
17851            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
17852        );
17853        assert!(
17854            declarations
17855                .iter()
17856                .all(|unit| !unit.identifier().is_empty()),
17857            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
17858        );
17859    }
17860
17861    #[test]
17862    fn object_like_field_macros_materialize_owner_specific_declarations() {
17863        let source = r#"#define PUBLIC_FIELDS int public_value;
17864#define PRIVATE_FIELDS int private_value;
17865#define NOT_A_FIELD_LIST not a declaration
17866
17867struct First {
17868  PUBLIC_FIELDS
17869  PRIVATE_FIELDS
17870};
17871struct Second {
17872  PUBLIC_FIELDS
17873  NOT_A_FIELD_LIST
17874};
17875#undef PUBLIC_FIELDS
17876struct Third {
17877  PUBLIC_FIELDS
17878};
17879"#;
17880        let parsed = parse_cpp_declarations(source, "macro-fields.c");
17881        let fields = parsed
17882            .declarations()
17883            .iter()
17884            .filter(|unit| unit.is_field())
17885            .map(|unit| unit.fq_name())
17886            .collect::<Vec<_>>();
17887
17888        assert!(
17889            fields.contains(&"First.public_value".to_string()),
17890            "{fields:?}"
17891        );
17892        assert!(
17893            fields.contains(&"First.private_value".to_string()),
17894            "{fields:?}"
17895        );
17896        assert!(
17897            fields.contains(&"Second.public_value".to_string()),
17898            "{fields:?}"
17899        );
17900        assert!(
17901            !fields.iter().any(|field| field.contains("not_a_field")),
17902            "malformed macro must fail closed: {fields:?}"
17903        );
17904        assert!(
17905            !fields.iter().any(|field| field.starts_with("Third.")),
17906            "undefined macro must fail closed: {fields:?}"
17907        );
17908    }
17909
17910    #[test]
17911    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
17912        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
17913        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
17914        let mut macros = parsed
17915            .declarations()
17916            .iter()
17917            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
17918            .collect::<Vec<_>>();
17919        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
17920
17921        assert_eq!(macros.len(), 2, "{macros:#?}");
17922        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
17923        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
17924        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
17925        assert_eq!(
17926            parsed.declaration_ranges(macros[1])[0].start_byte,
17927            source.rfind("#define VALUE 2").expect("second definition")
17928        );
17929    }
17930
17931    #[test]
17932    fn identifies_export_macro_class_base_displaced_into_declarator() {
17933        let source = r#"#define PROJECT_API_
17934namespace project {
17935namespace internal {
17936template <typename T>
17937class Base {};
17938}
17939template <typename T>
17940class Wrapper;
17941template <>
17942class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
17943}
17944"#;
17945        let mut parser = tree_sitter::Parser::new();
17946        parser
17947            .set_language(&tree_sitter_cpp::LANGUAGE.into())
17948            .unwrap();
17949        let tree = parser.parse(source, None).unwrap();
17950        let start = source.find("internal::Base<int>").expect("base");
17951        let mut base = tree
17952            .root_node()
17953            .descendant_for_byte_range(start, start + 8)
17954            .expect("base syntax");
17955        while base.kind() != "qualified_identifier" {
17956            base = base.parent().expect("qualified base ancestor");
17957        }
17958        assert!(
17959            is_recovered_exported_class_base_type_node(base, source),
17960            "{}",
17961            tree.root_node().to_sexp()
17962        );
17963    }
17964
17965    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
17966        let mut identities = parsed
17967            .declarations()
17968            .iter()
17969            .filter(|unit| unit.is_function())
17970            .map(|unit| {
17971                (
17972                    unit.fq_name(),
17973                    unit.signature().unwrap_or_default().to_string(),
17974                )
17975            })
17976            .collect::<Vec<_>>();
17977        identities.sort();
17978        identities
17979    }
17980
17981    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
17982    /// prototype macros. Each case is parsed independently so a preceding
17983    /// malformed line cannot flatten the next declaration's fields into one
17984    /// cascading `ERROR`; that shape has no structural proof and must fail
17985    /// closed rather than fall back to a token scan. Parameter names are
17986    /// dropped from signatures just as they are for ordinary C prototypes.
17987    #[test]
17988    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
17989        let cases = [
17990            (
17991                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
17992                "pg_typemap_fit_to_result",
17993                "(VALUE, VALUE)",
17994            ),
17995            (
17996                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
17997                "pg_typemap_result_value",
17998                "(t_typemap *, VALUE, int, int)",
17999            ),
18000            (
18001                "void pg_typemap_mark _(( void * ));",
18002                "pg_typemap_mark",
18003                "(void *)",
18004            ),
18005            (
18006                "void init_pg_type_map _(( void ));",
18007                "init_pg_type_map",
18008                "(void)",
18009            ),
18010            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
18011            (
18012                "extern VALUE pg_extern _(( VALUE, VALUE ));",
18013                "pg_extern",
18014                "(VALUE, VALUE)",
18015            ),
18016            (
18017                "size_t pg_typemap_memsize _(( const void * ));",
18018                "pg_typemap_memsize",
18019                "(const void *)",
18020            ),
18021            (
18022                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
18023                "pg_wrap_socket_io",
18024                "(int, VALUE, VALUE *, int *)",
18025            ),
18026            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
18027            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
18028            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
18029        ];
18030
18031        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
18032            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
18033            assert_eq!(
18034                function_identities(&parsed),
18035                vec![(name.to_string(), signature.to_string())],
18036                "{source}: {:#?}",
18037                parsed.declarations()
18038            );
18039            assert!(
18040                parsed.declarations().iter().all(|unit| !unit.is_field()),
18041                "{source} must not retain the malformed field: {:#?}",
18042                parsed.declarations()
18043            );
18044        }
18045    }
18046
18047    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
18048    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
18049    /// it as a multiplication of a type by a call
18050    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
18051    /// wrapped in an `expression_statement` that `has_error()` only because
18052    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
18053    /// here with a clean preceding field so the prototype remains the
18054    /// parser-owned `expression_statement` this recovery requires.
18055    #[test]
18056    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
18057        let cases = [
18058            (
18059                "PGconn *pg_get_pgconn _(( VALUE ));",
18060                "pg_get_pgconn",
18061                "(VALUE)",
18062            ),
18063            (
18064                "PGresult* pgresult_get _(( VALUE ));",
18065                "pgresult_get",
18066                "(VALUE)",
18067            ),
18068        ];
18069        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
18070            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
18071            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
18072            assert_eq!(
18073                function_identities(&parsed),
18074                vec![(name.to_string(), signature.to_string())],
18075                "{source}: {:#?}",
18076                parsed.declarations()
18077            );
18078            assert_eq!(
18079                parsed
18080                    .declarations()
18081                    .iter()
18082                    .filter(|unit| unit.is_field())
18083                    .map(|unit| unit.fq_name())
18084                    .collect::<Vec<_>>(),
18085                vec!["rb_mPG".to_string()],
18086                "{source}: {:#?}",
18087                parsed.declarations()
18088            );
18089        }
18090    }
18091
18092    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
18093    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
18094    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
18095    /// regions. The witnessed declaration must still become a Function and
18096    /// its type must not survive as the name of a bogus Field. This test does
18097    /// not claim that structurally flattened neighboring prototypes are safe
18098    /// to recover.
18099    #[test]
18100    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
18101        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
18102VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
18103int pg_typemap_fit_to_copy_get                         _(( VALUE ));
18104VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
18105t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
18106VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
18107void pg_typemap_mark                                   _(( void * ));
18108size_t pg_typemap_memsize                              _(( const void * ));
18109void pg_typemap_compact                                _(( void * ));
18110
18111PGconn *pg_get_pgconn                                  _(( VALUE ));
18112t_pg_connection *pg_get_connection                     _(( VALUE ));
18113VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
18114#ifdef __GNUC__
18115__attribute__((format(printf, 3, 4)))
18116#endif
18117NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
18118VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
18119void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
18120
18121
18122VALUE pg_new_result                                    _(( PGresult *, VALUE ));
18123VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
18124PGresult* pgresult_get                                 _(( VALUE ));
18125VALUE pg_result_check                                  _(( VALUE ));
18126VALUE pg_result_clear                                  _(( VALUE ));
18127VALUE pg_tuple_new                                     _(( VALUE, int ));
18128
18129/*
18130 * Fetch the data pointer for the result object
18131 */
18132static inline t_pg_result *
18133pgresult_get_this( VALUE self )
18134{
18135	return RTYPEDDATA_DATA(self);
18136}
18137
18138
18139rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
18140const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
18141rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
18142
18143"#;
18144        let parsed = parse_cpp_declarations(source, "pg.h");
18145        assert!(
18146            function_identities(&parsed)
18147                .iter()
18148                .any(|(name, signature)| name == "pg_typemap_result_value"
18149                    && signature == "(t_typemap *, VALUE, int, int)"),
18150            "the real issue witness must be a Function with its C signature: {:#?}",
18151            parsed.declarations()
18152        );
18153        assert!(
18154            parsed
18155                .declarations()
18156                .iter()
18157                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
18158            "the issue witness must not leave its return type as a Field name: {:#?}",
18159            parsed.declarations()
18160        );
18161    }
18162
18163    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
18164    /// between `explicit` and a constructor name onto the name, producing a
18165    /// `qualified_identifier` whose `::` it had to invent. The declared member
18166    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
18167    /// macro in type position, was already recovered and is the control that
18168    /// both spellings agree.
18169    #[test]
18170    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
18171        let source = r#"class SIMD_4x26 final {
18172   public:
18173      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
18174      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
18175      int m_v;
18176};
18177"#;
18178        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
18179        assert_eq!(
18180            function_identities(&parsed),
18181            vec![
18182                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
18183                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
18184            ],
18185            "{:#?}",
18186            parsed.declarations()
18187        );
18188    }
18189
18190    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
18191    /// `ERROR` holding the wrapped declaration and every declaration after it
18192    /// until the parser recovers. All of them must be indexed, each with the
18193    /// byte range that spells it.
18194    #[test]
18195    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
18196        let source = r#"#include <cstdint>
18197struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
18198    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
18199                     struct llama_model * model,
18200              struct llama_context_params   params),
18201            "use llama_init_from_model instead");
18202    LLAMA_API int32_t llama_tokenize(
18203        const struct llama_vocab * vocab,
18204                      const char * text,
18205                            bool   parse_special);
18206    LLAMA_API int32_t llama_other(int a);
18207"#;
18208        let parsed = parse_cpp_declarations(source, "llama.h");
18209        assert_eq!(
18210            function_identities(&parsed),
18211            vec![
18212                (
18213                    "llama_new_context_with_model".to_string(),
18214                    "(struct llama_model *, struct llama_context_params)".to_string()
18215                ),
18216                ("llama_other".to_string(), "(int)".to_string()),
18217                (
18218                    "llama_tokenize".to_string(),
18219                    "(const struct llama_vocab *, const char *, bool)".to_string()
18220                ),
18221            ],
18222            "{:#?}",
18223            parsed.declarations()
18224        );
18225
18226        // Each recovered declaration owns the source that spells it, so
18227        // navigation lands on the declaration and not on the macro envelope.
18228        for (name, expected) in [
18229            (
18230                "llama_new_context_with_model",
18231                "LLAMA_API struct llama_context * llama_new_context_with_model(",
18232            ),
18233            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
18234            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
18235        ] {
18236            let unit = parsed
18237                .declarations()
18238                .iter()
18239                .find(|unit| unit.is_function() && unit.fq_name() == name)
18240                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
18241            let [range] = parsed.declaration_ranges(unit) else {
18242                panic!("{name} must have exactly one range");
18243            };
18244            let text = &source[range.start_byte..range.end_byte];
18245            assert!(
18246                text.starts_with(expected),
18247                "{name} range is {text:?}, expected it to start with {expected:?}"
18248            );
18249            assert!(
18250                text.ends_with(')') || text.ends_with(';'),
18251                "{name}: {text:?}"
18252            );
18253        }
18254    }
18255
18256    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
18257    /// invocation's `(` at the end of its line, and the parser then keeps no
18258    /// invocation whole. It hands the first one's hint and its own `)` and `;`
18259    /// to the `expression_statement` beside it, and flattens the ones after
18260    /// that into bare `identifier`/`type_identifier`, `(`,
18261    /// `parameter_declaration`, `,` and `ERROR` parts of whatever node it is
18262    /// already inside. No part of that is a head the grouping can be read from,
18263    /// which is why the invocation is read from the token order instead
18264    /// (#3094).
18265    ///
18266    /// The identifiers are `library_*` at whisper's own lengths on purpose:
18267    /// tree-sitter prices error recovery partly by how much input a repair
18268    /// skips, and the same construct written with shorter names parses cleanly.
18269    #[test]
18270    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
18271        let source = r#"    LIBRARY_DEPRECATED(
18272        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
18273        "use library_init_from_file_with_params instead"
18274    );
18275    LIBRARY_DEPRECATED(
18276        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
18277        "use library_init_from_buffer_with_params instead"
18278    );
18279    LIBRARY_DEPRECATED(
18280        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
18281        "use library_init_with_params instead"
18282    );
18283"#;
18284        let mut parser = tree_sitter::Parser::new();
18285        parser
18286            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18287            .expect("C++ grammar");
18288        let tree = parser.parse(source, None).expect("C++ tree");
18289        let root = tree.root_node();
18290
18291        // The shape this recovery exists for: the second invocation is not a
18292        // node of its own, and the third is a run of bare siblings inside the
18293        // second one's `ERROR`.
18294        let mut cursor = root.walk();
18295        let items = root.children(&mut cursor).collect::<Vec<_>>();
18296        let [first, hint_statement, rest @ ..] = items.as_slice() else {
18297            panic!("{}", root.to_sexp());
18298        };
18299        assert_eq!(first.kind(), "ERROR");
18300        assert_eq!(hint_statement.kind(), "expression_statement");
18301        assert!(
18302            node_text(*first, source).ends_with(','),
18303            "the first invocation's own `)` and `;` are the statement's, not its own: {}",
18304            root.to_sexp()
18305        );
18306        let swallowing = rest
18307            .iter()
18308            .find(|item| item.kind() == "ERROR")
18309            .unwrap_or_else(|| panic!("{}", root.to_sexp()));
18310        let mut swallowing_cursor = swallowing.walk();
18311        let flattened = swallowing
18312            .children(&mut swallowing_cursor)
18313            .map(|child| child.kind())
18314            .collect::<Vec<_>>();
18315        assert_eq!(
18316            flattened,
18317            vec![
18318                "identifier",
18319                "(",
18320                "parameter_declaration",
18321                ",",
18322                "ERROR",
18323                "type_identifier",
18324                "(",
18325                "parameter_declaration",
18326                ",",
18327                "\"",
18328                "identifier",
18329                "identifier",
18330                "identifier",
18331                "\"",
18332                ")",
18333            ],
18334            "the third invocation must be flattened into the second one's node: {}",
18335            root.to_sexp()
18336        );
18337
18338        // Both are admitted, and each names the byte just past its own `;`.
18339        let ancestry = ParentIndex::new(root);
18340        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
18341            .expect("the first invocation");
18342        assert_eq!(
18343            &source[..first_run.invocation_end],
18344            &source[..source.find("instead\"\n    );").expect("first hint")
18345                + "instead\"\n    );".len()]
18346        );
18347        assert_eq!(
18348            first_run.region_end, first_run.invocation_end,
18349            "the first invocation swallowed nothing, so the recovery owns only its own bytes"
18350        );
18351        let swallowing_run = collapsed_macro_declaration_run(*swallowing, source, &ancestry)
18352            .expect("the second invocation");
18353        assert!(
18354            swallowing_run.invocation_end < swallowing.end_byte(),
18355            "the second invocation swallowed the third"
18356        );
18357        assert_eq!(
18358            swallowing_run.region_end,
18359            root.end_byte(),
18360            "a swallowing invocation owns the region to the close of its declaration scope"
18361        );
18362
18363        let parsed = parse_cpp_declarations(source, "library.h");
18364        assert_eq!(
18365            function_identities(&parsed),
18366            vec![
18367                (
18368                    "library_init".to_string(),
18369                    "(struct library_model_loader *)".to_string()
18370                ),
18371                (
18372                    "library_init_from_buffer".to_string(),
18373                    "(void *, size_t)".to_string()
18374                ),
18375                (
18376                    "library_init_from_file".to_string(),
18377                    "(const char *)".to_string()
18378                ),
18379            ],
18380            "{:#?}",
18381            parsed.declarations()
18382        );
18383    }
18384
18385    /// The hand-off that keeps the scan finite. An invocation that reaches the
18386    /// end of its own declaration scope swallowed nothing, and the ordinary
18387    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
18388    /// that region for every invocation it finds, so admitting it here would
18389    /// hand the scan its own input back.
18390    #[test]
18391    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
18392        let source = r#"LIBRARY_DEPRECATED(
18393        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
18394        "use library_init_from_file_with_params instead"
18395    );"#;
18396        let mut parser = tree_sitter::Parser::new();
18397        parser
18398            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18399            .expect("C++ grammar");
18400        let tree = parser.parse(source, None).expect("C++ tree");
18401        let root = tree.root_node();
18402        let head = root.named_child(0).expect("the invocation");
18403        let ancestry = ParentIndex::new(root);
18404        assert!(
18405            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
18406            "{}",
18407            root.to_sexp()
18408        );
18409        assert!(
18410            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
18411            "the ordinary reader must be the one that has it: {}",
18412            root.to_sexp()
18413        );
18414        // The index is a speed substitution, never a semantic one: the
18415        // unindexed path is `Node::parent` itself and must agree.
18416        let unindexed = ParentIndex::unindexed();
18417        assert!(
18418            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
18419            "indexed and unindexed climbs must agree"
18420        );
18421        assert_eq!(
18422            macro_wrapped_declarations(head, source, &ancestry).len(),
18423            macro_wrapped_declarations(head, source, &unindexed).len(),
18424            "indexed and unindexed climbs must agree"
18425        );
18426    }
18427
18428    /// Negative controls for the same recovery: a macro invocation whose
18429    /// arguments are not a declaration recovers nothing, whether the parser
18430    /// keeps it clean, reads the arguments as a type, or reads them as a bare
18431    /// declarator.
18432    #[test]
18433    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
18434        for source in [
18435            "int before;\nFOO(1, 2);\nint after;\n",
18436            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
18437            "int before;\nMACRO(int a, int b);\nint after;\n",
18438            "DECLARE_HANDLE(HWND);\nint after;\n",
18439        ] {
18440            let parsed = parse_cpp_declarations(source, "macro-call.h");
18441            assert_eq!(
18442                function_identities(&parsed),
18443                Vec::new(),
18444                "{source:?} must declare no function: {:#?}",
18445                parsed.declarations()
18446            );
18447        }
18448    }
18449
18450    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
18451    /// makes tree-sitter read the macro as the member's type and its argument
18452    /// list as a parenthesized declarator, which then swallows the attributed
18453    /// member and the member written after it. Both are members.
18454    #[test]
18455    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
18456        let source = r#"#include <string_view>
18457namespace Botan {
18458class DL_Group final {
18459   public:
18460      DL_Group() = default;
18461      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
18462      DL_Group(std::string_view pem, int format);
18463      size_t get_p() const;
18464};
18465}
18466"#;
18467        let parsed = parse_cpp_declarations(source, "dl_group.h");
18468        assert_eq!(
18469            function_identities(&parsed),
18470            vec![
18471                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
18472                (
18473                    "Botan.DL_Group.DL_Group".to_string(),
18474                    "(std::string_view)".to_string()
18475                ),
18476                (
18477                    "Botan.DL_Group.DL_Group".to_string(),
18478                    "(std::string_view, int)".to_string()
18479                ),
18480                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18481            ],
18482            "{:#?}",
18483            parsed.declarations()
18484        );
18485    }
18486
18487    /// #2552 shape 3, export-macro class. The class head macro makes the body a
18488    /// `compound_statement`, so members come from the region reparse, and one
18489    /// string-attribute member used to make that reparse unindexable -- which
18490    /// left the class with no members at all.
18491    #[test]
18492    fn an_export_macro_class_keeps_its_string_attribute_members() {
18493        let source = r#"#include <string_view>
18494namespace Botan {
18495class BOTAN_PUBLIC_API(2, 0) DL_Group final {
18496   public:
18497      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
18498      DL_Group(std::string_view pem, int format);
18499      size_t get_p() const;
18500};
18501}
18502"#;
18503        let parsed = parse_cpp_declarations(source, "dl_group.h");
18504        assert!(
18505            parsed
18506                .declarations()
18507                .iter()
18508                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
18509            "{:#?}",
18510            parsed.declarations()
18511        );
18512        assert_eq!(
18513            function_identities(&parsed),
18514            vec![
18515                (
18516                    "Botan.DL_Group.DL_Group".to_string(),
18517                    "(std::string_view)".to_string()
18518                ),
18519                (
18520                    "Botan.DL_Group.DL_Group".to_string(),
18521                    "(std::string_view, int)".to_string()
18522                ),
18523                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
18524            ],
18525            "{:#?}",
18526            parsed.declarations()
18527        );
18528    }
18529
18530    /// #2552 shape 3, the `= default` variant plus the access-label
18531    /// constructor. The attributed defaulted constructor separates cleanly, but
18532    /// it strands the next member in a bare `ERROR`, and a constructor written
18533    /// with a member-initializer list under `private:` dissolves into
18534    /// expression soup that the reparse recovers from its own source range.
18535    #[test]
18536    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
18537        let source = r#"namespace Botan {
18538class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
18539   public:
18540      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
18541      XMSS_Parameters(int oid, int len);
18542      size_t len() const;
18543
18544   private:
18545      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
18546            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
18547
18548      int m_oid;
18549      int m_wots_oid;
18550      size_t m_element_size;
18551      size_t m_tree_height;
18552};
18553}
18554"#;
18555        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
18556        let constructors = function_identities(&parsed)
18557            .into_iter()
18558            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
18559            .map(|(_, signature)| signature)
18560            .collect::<Vec<_>>();
18561        assert_eq!(
18562            constructors,
18563            vec![
18564                "()".to_string(),
18565                "(int, int)".to_string(),
18566                "(int, int, size_t, size_t)".to_string(),
18567            ],
18568            "{:#?}",
18569            parsed.declarations()
18570        );
18571    }
18572
18573    /// Negative control for the same rule: a real qualified name spells its
18574    /// `::` in the source, so the separator is present rather than MISSING and
18575    /// the out-of-line definition keeps its owner.
18576    #[test]
18577    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
18578        let source = r#"namespace shell {
18579struct Outer {
18580   struct Inner {
18581      Inner(int v);
18582      void run(int v);
18583   };
18584};
18585Outer::Inner::Inner(int v) {}
18586void Outer::Inner::run(int v) {}
18587}
18588"#;
18589        let parsed = parse_cpp_declarations(source, "outer.cpp");
18590        let names = function_identities(&parsed)
18591            .into_iter()
18592            .map(|(fq_name, _)| fq_name)
18593            .collect::<Vec<_>>();
18594        assert!(
18595            names
18596                .iter()
18597                .all(|name| name.starts_with("shell.Outer$Inner.")),
18598            "{names:#?}"
18599        );
18600    }
18601
18602    #[test]
18603    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
18604        let source = r#"namespace control {
18605template <typename T>
18606class AnySpan;
18607template <typename T>
18608class ABSL_ATTRIBUTE_VIEW AnySpan {
18609 public:
18610  int begin() const;
18611};
18612}
18613
18614namespace absl {
18615ABSL_NAMESPACE_BEGIN
18616template <typename T>
18617class ABSL_ATTRIBUTE_VIEW Span {
18618 public:
18619  int begin() const;
18620  int back() const;
18621};
18622
18623int begin();
18624int back();
18625}
18626"#;
18627        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
18628        let declarations = parsed.declarations();
18629        assert!(
18630            declarations
18631                .iter()
18632                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
18633        );
18634        for method in ["begin", "back"] {
18635            assert!(declarations.iter().any(|unit| {
18636                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
18637            }));
18638            assert!(
18639                declarations.iter().any(|unit| {
18640                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
18641                })
18642            );
18643        }
18644        assert!(
18645            declarations
18646                .iter()
18647                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
18648        );
18649        assert!(
18650            declarations
18651                .iter()
18652                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
18653        );
18654        assert!(
18655            declarations
18656                .iter()
18657                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
18658        );
18659    }
18660
18661    #[test]
18662    fn explicit_global_member_definition_has_canonical_package_boundary() {
18663        let source = r#"
18664namespace arangodb::aql {
18665class ExecutionPlan {
18666 public:
18667  template<class... Args> Node* createNode(Args&&... args);
18668};
18669}
18670
18671template<class... Args>
18672Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
18673"#;
18674        let parsed = parse_cpp_declarations(source, "global-member.cpp");
18675
18676        assert!(parsed.declarations().iter().any(|unit| {
18677            unit.is_function()
18678                && unit.package_name() == "arangodb::aql"
18679                && unit.short_name() == "ExecutionPlan.createNode"
18680                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
18681        }));
18682    }
18683
18684    #[test]
18685    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
18686        let source = r#"
18687#ifndef TINYXML2_INCLUDED
18688#define TINYXML2_INCLUDED
18689namespace tinyxml2 {
18690class TINYXML2_LIB XMLUtil {
18691 public:
18692  static const char* SkipWhiteSpace(const char* p) {
18693    while (*p) {
18694      if (*p == ' ') {
18695        ++p;
18696      }
18697    }
18698    return p;
18699  }
18700  static bool StringEqual(const char* p, const char* q) {
18701    return p == q;
18702  }
18703  class TINYXML2_LIB Helper {
18704   public:
18705    void Touch();
18706  };
18707  static void ToStr(int value, char* buffer);
18708 private:
18709  static const char* writeBoolTrue;
18710};
18711
18712class TINYXML2_LIB XMLNode {
18713 public:
18714  virtual XMLNode* ShallowClone() const = 0;
18715  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
18716};
18717}
18718#endif
18719"#;
18720        let mut parser = tree_sitter::Parser::new();
18721        parser
18722            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18723            .unwrap();
18724        let tree = parser.parse(source, None).unwrap();
18725        let mut boundary_found = false;
18726        walk_named_tree_preorder(tree.root_node(), true, |node| {
18727            if let Some((_, name, _)) = recover_exported_class_function_definition(node, source)
18728                && name == "XMLUtil"
18729            {
18730                boundary_found = fragmented_export_sibling_class_boundary(
18731                    node,
18732                    source,
18733                    &ParentIndex::unindexed(),
18734                )
18735                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
18736                .is_some_and(|(_, name, _)| name == "XMLNode");
18737            }
18738            WalkControl::Continue
18739        });
18740        assert!(
18741            boundary_found,
18742            "fixture must exercise the recovered sibling boundary"
18743        );
18744
18745        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
18746        assert!(
18747            parsed
18748                .declarations()
18749                .iter()
18750                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
18751            "{:#?}",
18752            parsed.declarations()
18753        );
18754        assert!(
18755            parsed
18756                .declarations()
18757                .iter()
18758                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
18759            "{:#?}",
18760            parsed.declarations()
18761        );
18762        assert!(parsed.declarations().iter().any(|unit| {
18763            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
18764        }));
18765        assert!(
18766            parsed
18767                .declarations()
18768                .iter()
18769                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
18770        );
18771        assert!(
18772            parsed
18773                .declarations()
18774                .iter()
18775                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
18776        );
18777    }
18778
18779    #[test]
18780    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
18781        // Clang's diagnostic suite intentionally contains this ill-formed
18782        // spelling. The analyzer must retain the parser's explicit-global AST
18783        // boundary instead of constructing `cwg311::::cwg311::X`.
18784        let parsed = parse_cpp_declarations(
18785            r#"
18786namespace cwg311 {
18787namespace X { namespace Y {} }
18788namespace ::cwg311::X {}
18789}
18790"#,
18791            "explicit-global-namespace.cpp",
18792        );
18793
18794        assert!(parsed.declarations().iter().any(|unit| {
18795            unit.kind() == CodeUnitType::Module
18796                && unit.short_name() == "cwg311::X"
18797                && unit.fq_name() == "cwg311::X"
18798        }));
18799        assert!(
18800            parsed
18801                .declarations()
18802                .iter()
18803                .all(|unit| !unit.short_name().contains("::::")),
18804            "recovered namespace names must not retain empty scope components: {:#?}",
18805            parsed.declarations()
18806        );
18807    }
18808
18809    #[test]
18810    fn repeated_scope_separator_does_not_create_empty_function_owner() {
18811        let scope = ScopeInfo {
18812            package_name: "X".to_string(),
18813            module: None,
18814            class_unit: None,
18815            template_signature: None,
18816            template_metadata: None,
18817            declarations_are_fields: false,
18818            recovered_specialization_member_scope: false,
18819            visible_using_namespaces: Vec::new(),
18820        };
18821
18822        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
18823
18824        assert!(owner.is_none());
18825        assert_eq!(name, "doit");
18826        assert_eq!(package, "X");
18827    }
18828
18829    #[test]
18830    fn trailing_decltype_expression_is_not_a_function_declarator() {
18831        let source = r#"
18832namespace boost { namespace detail {
18833#if ! defined(BOOST_NO_SFINAE_EXPR) && \
18834    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
18835    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
18836#define BOOST_THREAD_PROVIDES_INVOKE
18837#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
18838template <class Fp, class A0, class ...Args>
18839inline auto
18840invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
18841       BOOST_THREAD_RV_REF(Args) ...args)
18842    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
18843{
18844    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
18845}
18846#endif
18847#endif
18848}}
18849"#;
18850        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
18851
18852        assert!(
18853            parsed
18854                .declarations()
18855                .iter()
18856                .all(|unit| unit.short_name() != ".*f")
18857        );
18858    }
18859
18860    fn find_class_named<'tree>(
18861        root: Node<'tree>,
18862        source: &str,
18863        expected_name: &str,
18864    ) -> Option<Node<'tree>> {
18865        let mut stack = vec![root];
18866        while let Some(node) = stack.pop() {
18867            if node.kind() == "class_specifier"
18868                && node
18869                    .child_by_field_name("name")
18870                    .is_some_and(|name| node_text(name, source) == expected_name)
18871            {
18872                return Some(node);
18873            }
18874            let mut cursor = node.walk();
18875            stack.extend(node.named_children(&mut cursor));
18876        }
18877        None
18878    }
18879
18880    #[test]
18881    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
18882        let source = r#"EXPORT void definition(struct Value value) {}
18883EXPORT void prototype(struct Value value);
18884"#;
18885        let mut parser = tree_sitter::Parser::new();
18886        parser
18887            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18888            .unwrap();
18889        let tree = parser.parse(source, None).unwrap();
18890        let root = tree.root_node();
18891        let mut cursor = root.walk();
18892        let callables = root
18893            .named_children(&mut cursor)
18894            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
18895            .collect::<Vec<_>>();
18896
18897        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
18898        for callable in callables {
18899            assert!(callable.has_error(), "fixture must exercise error recovery");
18900            assert!(
18901                cpp_sentinel_macro_parts(callable, source).is_none(),
18902                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
18903            );
18904        }
18905    }
18906
18907    #[test]
18908    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
18909        let source = r#"namespace absl {
18910ABSL_NAMESPACE_BEGIN
18911// Generate a floating-point variate conforming to a Beta distribution:
18912template <typename RealType = double>
18913class beta_distribution {
18914 public:
18915  using result_type = RealType;
18916
18917
18918  beta_distribution() : beta_distribution(1) {}
18919
18920  explicit beta_distribution(result_type alpha, result_type beta = 1)
18921      : param_(alpha, beta) {}
18922
18923  explicit beta_distribution(const param_type& p) : param_(p) {}
18924
18925  void reset() {}
18926
18927  // Generating functions
18928  template <typename URBG>
18929  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
18930    return (*this)(g, param_);
18931  }
18932
18933};
18934ABSL_NAMESPACE_END
18935}  // namespace absl
18936"#;
18937        let mut parser = tree_sitter::Parser::new();
18938        parser
18939            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18940            .unwrap();
18941        let tree = parser.parse(source, None).unwrap();
18942        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18943        let body = namespace
18944            .child_by_field_name("body")
18945            .expect("fixture namespace body");
18946        let sentinel = body.named_child(0).expect("sentinel envelope");
18947        let callable = sentinel
18948            .child_by_field_name("declarator")
18949            .and_then(extract_function_declarator)
18950            .and_then(cpp_function_declarator_name_node)
18951            .expect("preserved callable name");
18952
18953        assert_eq!(sentinel.kind(), "function_definition");
18954        assert_eq!(callable.kind(), "operator_name");
18955        assert!(
18956            cpp_sentinel_macro_parts(sentinel, source).is_some(),
18957            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
18958        );
18959    }
18960
18961    #[test]
18962    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
18963        let source = r#"namespace absl {
18964ABSL_NAMESPACE_BEGIN
18965// absl::discrete_distribution
18966//
18967// A discrete distribution produces random integers i, where 0 <= i < n
18968template <typename IntType = int>
18969class discrete_distribution {
18970 public:
18971  using result_type = IntType;
18972  class param_type {
18973   public:
18974    param_type() { init(); }
18975    template <typename InputIterator>
18976    explicit param_type(InputIterator begin, InputIterator end)
18977        : p_(begin, end) {
18978      init();
18979    }
18980  };
18981  discrete_distribution() : param_() {}
18982  explicit discrete_distribution(const param_type& p) : param_(p) {}
18983};
18984ABSL_NAMESPACE_END
18985}  // namespace absl
18986"#;
18987        let mut parser = tree_sitter::Parser::new();
18988        parser
18989            .set_language(&tree_sitter_cpp::LANGUAGE.into())
18990            .unwrap();
18991        let tree = parser.parse(source, None).unwrap();
18992        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
18993        let body = namespace
18994            .child_by_field_name("body")
18995            .expect("fixture namespace body");
18996        let sentinel = body.named_child(0).expect("sentinel envelope");
18997        let callable = sentinel
18998            .child_by_field_name("declarator")
18999            .and_then(extract_function_declarator)
19000            .and_then(cpp_function_declarator_name_node)
19001            .expect("preserved callable name");
19002
19003        assert_eq!(sentinel.kind(), "function_definition");
19004        assert_eq!(callable.kind(), "identifier");
19005        assert!(
19006            cpp_sentinel_macro_parts(sentinel, source).is_some(),
19007            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
19008        );
19009    }
19010
19011    #[test]
19012    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
19013        let source = r#"
19014#define CPPCHECKLIB
19015class Library {
19016    struct Container {
19017        CPPCHECKLIB static std::string toString(Yield yield);
19018        CPPCHECKLIB static std::string toString(Action action);
19019    };
19020};
19021"#;
19022        let mut parser = tree_sitter::Parser::new();
19023        parser
19024            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19025            .unwrap();
19026        let tree = parser.parse(source, None).unwrap();
19027        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
19028        let parsed = parse_cpp_file(&file, source, &tree);
19029        assert!(
19030            parsed
19031                .declarations()
19032                .iter()
19033                .all(|unit| unit.fq_name() != "Library$Container.std"),
19034            "the qualified return-type namespace must not become a field: {:#?}",
19035            parsed.declarations()
19036        );
19037        for expected in ["(Yield)", "(Action)"] {
19038            assert!(
19039                parsed.declarations().iter().any(|unit| {
19040                    unit.is_function()
19041                        && unit.fq_name() == "Library$Container.toString"
19042                        && unit.signature() == Some(expected)
19043                }),
19044                "recovered toString overload {expected} is missing: {:#?}",
19045                parsed.declarations()
19046            );
19047        }
19048    }
19049
19050    #[test]
19051    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
19052        let source = r#"
19053#define SIMPLECPP_LIB
19054namespace simplecpp {
19055using TokenString = std::string;
19056struct Location { int line{}; };
19057class SIMPLECPP_LIB Token {
19058  TokenString prefix;
19059  void prefix_method() {}
19060 public:
19061  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
19062      whitespaceahead(wsahead), location(loc), string(s)
19063      // The comment must not hide the constructor body from recovery.
19064      {
19065      flags();
19066  }
19067  TokenString string;
19068  bool whitespaceahead;
19069  Location location;
19070  Token *previous{};
19071 private:
19072  void flags() {
19073      whitespaceahead = true;
19074  }
19075};
19076}
19077"#;
19078        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
19079
19080        let location_fields = parsed
19081            .declarations()
19082            .iter()
19083            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
19084            .collect::<Vec<_>>();
19085        assert_eq!(
19086            location_fields.len(),
19087            1,
19088            "location should have one class-owned declaration: {:#?}",
19089            parsed.declarations()
19090        );
19091        assert!(
19092            location_fields[0].is_field(),
19093            "location has wrong kind: {:#?}",
19094            parsed.declarations()
19095        );
19096        assert!(
19097            parsed.declarations().iter().all(|unit| {
19098                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
19099            })
19100        );
19101        assert!(
19102            parsed.declarations().iter().all(|unit| {
19103                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
19104            })
19105        );
19106        assert!(
19107            parsed
19108                .declarations()
19109                .iter()
19110                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
19111        );
19112        assert!(
19113            parsed
19114                .declarations()
19115                .iter()
19116                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
19117            "the recovered class must retain its constructor: {:#?}",
19118            parsed.declarations()
19119        );
19120        assert!(
19121            parsed
19122                .declarations()
19123                .iter()
19124                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
19125        );
19126        assert!(parsed.declarations().iter().any(|unit| {
19127            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
19128        }));
19129        let constructor = parsed
19130            .declarations()
19131            .iter()
19132            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
19133            .expect("recovered constructor");
19134        let constructor_start = source.find("Token(const").expect("constructor start");
19135        let constructor_end = source
19136            .get(
19137                ..source
19138                    .find("  TokenString string;")
19139                    .expect("constructor end"),
19140            )
19141            .expect("constructor slice")
19142            .trim_end()
19143            .len();
19144        assert!(
19145            parsed
19146                .navigation_ranges
19147                .get(constructor)
19148                .is_some_and(|ranges| {
19149                    ranges.iter().any(|range| {
19150                        range.start_byte == constructor_start && range.end_byte == constructor_end
19151                    })
19152                }),
19153            "constructor navigation must span the full body: {:#?}",
19154            parsed.navigation_ranges
19155        );
19156        assert_eq!(
19157            parsed
19158                .signature_metadata
19159                .get(constructor)
19160                .and_then(|metadata| metadata.first())
19161                .and_then(SignatureMetadata::callable_linkage),
19162            Some(CallableLinkage::External)
19163        );
19164        let token_class = parsed
19165            .declarations()
19166            .iter()
19167            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
19168            .expect("recovered Token class");
19169        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
19170        assert!(
19171            parsed
19172                .navigation_ranges
19173                .get(token_class)
19174                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
19175            "class navigation must include the terminating semicolon: {:#?}",
19176            parsed.navigation_ranges
19177        );
19178    }
19179
19180    #[test]
19181    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
19182        let source = r#"
19183#define SIMPLECPP_LIB
19184namespace simplecpp {
19185using TokenString = std::string;
19186class Macro;
19187struct Location {
19188  unsigned int fileIndex{};
19189  unsigned int line{};
19190  unsigned int col{};
19191};
19192struct Output {
19193  int type;
19194};
19195class SIMPLECPP_LIB Token {
19196 public:
19197  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
19198      whitespaceahead(wsahead), location(loc), string(s) {
19199      flags();
19200  }
19201  Token(const Token &tok) :
19202      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
19203      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
19204      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
19205  Token &operator=(const Token &tok) = delete;
19206  const TokenString& str() const { return string; }
19207  void setstr(const std::string &s) { string = s; flags(); }
19208  bool isOneOf(const char ops[]) const;
19209  TokenString macro;
19210  char op;
19211  bool comment;
19212  bool name;
19213  bool number;
19214  bool whitespaceahead;
19215  Location location;
19216  Token *previous{};
19217  Token *next{};
19218 private:
19219  void flags() {
19220      name = !string.empty();
19221      comment = false;
19222      number = false;
19223      op = 0;
19224  }
19225  TokenString string;
19226};
19227}
19228struct Following {
19229  int type;
19230};
19231class SIMPLECPP_LIB Later {
19232 public:
19233  Later(int value) : value(value) {}
19234  int value;
19235};
19236"#;
19237        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
19238        assert!(
19239            parsed
19240                .declarations()
19241                .iter()
19242                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
19243        );
19244        assert!(
19245            !parsed
19246                .declarations()
19247                .iter()
19248                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
19249        );
19250        assert!(
19251            parsed
19252                .declarations()
19253                .iter()
19254                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
19255        );
19256        assert!(
19257            !parsed
19258                .declarations()
19259                .iter()
19260                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
19261        );
19262        assert!(
19263            parsed
19264                .declarations()
19265                .iter()
19266                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
19267        );
19268        assert!(
19269            parsed
19270                .declarations()
19271                .iter()
19272                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
19273        );
19274        assert!(
19275            parsed
19276                .declarations()
19277                .iter()
19278                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
19279        );
19280        assert!(
19281            parsed
19282                .declarations()
19283                .iter()
19284                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
19285        );
19286        assert!(
19287            parsed
19288                .declarations()
19289                .iter()
19290                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
19291        );
19292        assert!(
19293            parsed
19294                .declarations()
19295                .iter()
19296                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
19297        );
19298        assert!(parsed.declarations().iter().all(|unit| {
19299            !matches!(
19300                unit.fq_name().as_str(),
19301                "simplecpp.Token.Following" | "simplecpp.Token.Later"
19302            )
19303        }));
19304        assert!(
19305            !parsed
19306                .declarations()
19307                .iter()
19308                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
19309            "the following struct must remain outside the recovered Token class"
19310        );
19311    }
19312
19313    #[test]
19314    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
19315        let source = r#"
19316#define SIMPLECPP_LIB
19317namespace {
19318namespace simplecpp {
19319using TokenString = std::string;
19320struct Location { int line{}; };
19321class SIMPLECPP_LIB HiddenToken {
19322 public:
19323  HiddenToken(const TokenString &s, const Location &loc) :
19324      location(loc), string(s) {
19325      flags();
19326  }
19327  TokenString string;
19328  Location location;
19329  HiddenToken *previous{};
19330 private:
19331  void flags() {}
19332};
19333}
19334}
19335"#;
19336        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
19337        let constructor = parsed
19338            .declarations()
19339            .iter()
19340            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
19341            .expect("recovered anonymous-namespace constructor");
19342        assert_eq!(
19343            parsed
19344                .signature_metadata
19345                .get(constructor)
19346                .and_then(|metadata| metadata.first())
19347                .and_then(SignatureMetadata::callable_linkage),
19348            Some(CallableLinkage::Internal)
19349        );
19350    }
19351
19352    #[test]
19353    fn macro_qualified_static_field_keeps_real_declarator() {
19354        let source = r#"#define JSON_INLINE_VARIABLE
19355struct Reader {
19356static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
19357static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
19358static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
19359};"#;
19360        let mut parser = tree_sitter::Parser::new();
19361        parser
19362            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19363            .unwrap();
19364        let tree = parser.parse(source, None).unwrap();
19365        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
19366        let parsed = parse_cpp_file(&file, source, &tree);
19367        for expected in [
19368            "Reader.npos",
19369            "Reader.other",
19370            "Reader.pointer",
19371            "Reader.reference",
19372        ] {
19373            assert!(
19374                parsed
19375                    .declarations()
19376                    .iter()
19377                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
19378                "real macro-decorated field {expected} is missing: {:#?}",
19379                parsed.declarations()
19380            );
19381        }
19382        assert!(
19383            parsed
19384                .declarations()
19385                .iter()
19386                .all(|unit| unit.fq_name() != "Reader.std"),
19387            "qualified type prefix became a pseudo-field: {:#?}",
19388            parsed.declarations()
19389        );
19390        let root = tree.root_node();
19391        let mut stack = vec![root];
19392        let mut signatures = Vec::new();
19393        while let Some(current) = stack.pop() {
19394            if let Some(declarators) = recovered_macro_qualified_field_declarators(current, source)
19395            {
19396                signatures.extend(
19397                    declarators
19398                        .into_iter()
19399                        .map(|declarator| render_cpp_field_signature(current, declarator, source)),
19400                );
19401            }
19402            let mut cursor = current.walk();
19403            stack.extend(current.named_children(&mut cursor));
19404        }
19405        signatures.sort();
19406        assert_eq!(
19407            signatures,
19408            [
19409                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
19410                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
19411                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
19412                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
19413            ]
19414        );
19415    }
19416
19417    fn member_function_linkage(source: &str) -> CallableLinkage {
19418        let mut parser = tree_sitter::Parser::new();
19419        parser
19420            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19421            .unwrap();
19422        let tree = parser.parse(source, None).unwrap();
19423        let ancestry = ParentIndex::new(tree.root_node());
19424        let mut stack = vec![tree.root_node()];
19425        while let Some(node) = stack.pop() {
19426            if node.kind() == "function_definition" {
19427                let mut current = node.parent();
19428                while let Some(parent) = current {
19429                    if matches!(
19430                        parent.kind(),
19431                        "class_specifier" | "struct_specifier" | "union_specifier"
19432                    ) {
19433                        return cpp_callable_linkage(node, source, &ancestry);
19434                    }
19435                    current = parent.parent();
19436                }
19437            }
19438            let mut cursor = node.walk();
19439            stack.extend(node.named_children(&mut cursor));
19440        }
19441        panic!("fixture has no member function definition");
19442    }
19443
19444    #[test]
19445    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
19446        assert_eq!(
19447            member_function_linkage("struct Named { int method() { return 1; } };"),
19448            CallableLinkage::External
19449        );
19450        assert_eq!(
19451            member_function_linkage(
19452                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
19453            ),
19454            CallableLinkage::Internal
19455        );
19456        assert_eq!(
19457            member_function_linkage("struct { int method() { return 1; } } instance;"),
19458            CallableLinkage::Internal
19459        );
19460        assert_eq!(
19461            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
19462            CallableLinkage::Internal
19463        );
19464    }
19465
19466    #[test]
19467    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
19468        let source = r#"
19469#ifndef PROTON_VALUE_HPP
19470#define PROTON_VALUE_HPP
19471namespace proton {
19472namespace internal {
19473class value_base {
19474  protected:
19475    internal::data& data();
19476    internal::data data_;
19477  friend class codec::encoder;
19478  friend class codec::decoder;
19479};
19480}
19481class value : public internal::value_base, private internal::comparable<value> {
19482  private:
19483    template<class T, class U=void> struct assignable :
19484        public std::enable_if<codec::is_encodable<T>::value, U> {};
19485    template<class U> struct assignable<value, U> {};
19486  public:
19487    PN_CPP_EXTERN value();
19488    PN_CPP_EXTERN value(const value&);
19489    PN_CPP_EXTERN value& operator=(const value&);
19490    PN_CPP_EXTERN value(value&&);
19491    PN_CPP_EXTERN value& operator=(value&&);
19492    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
19493    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
19494        codec::encoder e(*this);
19495        e << x;
19496        return *this;
19497    }
19498    PN_CPP_EXTERN type_id type() const;
19499    PN_CPP_EXTERN bool empty() const;
19500    PN_CPP_EXTERN void clear();
19501    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
19502    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
19503  friend PN_CPP_EXTERN void swap(value&, value&);
19504  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
19505  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
19506  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
19507    value(pn_data_t* d);
19508    void reset(pn_data_t* d = 0);
19509};
19510}
19511#endif
19512"#;
19513        let mut parser = tree_sitter::Parser::new();
19514        parser
19515            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19516            .unwrap();
19517        let tree = parser.parse(source, None).unwrap();
19518        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
19519        let parsed = parse_cpp_file(&file, source, &tree);
19520        let macro_constructors = parsed
19521            .signature_metadata
19522            .iter()
19523            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value")
19524            .flat_map(|(_, metadata)| metadata)
19525            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
19526            .collect::<Vec<_>>();
19527
19528        assert_eq!(
19529            macro_constructors.len(),
19530            3,
19531            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
19532            parsed.declarations()
19533        );
19534        assert!(
19535            macro_constructors.iter().all(|metadata| {
19536                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
19537            }),
19538            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
19539        );
19540    }
19541
19542    #[test]
19543    fn recovered_export_class_typedef_uses_displaced_alias_name() {
19544        let source = r#"
19545namespace spi {
19546class Filter {
19547public:
19548    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
19549};
19550}
19551namespace filter {
19552class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
19553{
19554public:
19555    typedef spi::Filter BASE_CLASS;
19556    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
19557    BEGIN_LOG4CXX_CAST_MAP()
19558    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
19559    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
19560    END_LOG4CXX_CAST_MAP()
19561    FilterDecision decide() const;
19562};
19563}
19564"#;
19565        let mut parser = tree_sitter::Parser::new();
19566        parser
19567            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19568            .unwrap();
19569        let tree = parser.parse(source, None).unwrap();
19570        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
19571        let parsed = parse_cpp_file(&file, source, &tree);
19572        assert!(
19573            parsed.declarations().iter().any(|unit| {
19574                unit.is_class()
19575                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
19576                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
19577            }),
19578            "the displaced typedef alias must retain its declared name: {:#?}",
19579            parsed.declarations()
19580        );
19581        assert!(
19582            parsed
19583                .declarations()
19584                .iter()
19585                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
19586            "the qualified underlying type must not become a false nested alias: {:#?}",
19587            parsed.declarations()
19588        );
19589    }
19590
19591    #[test]
19592    fn exported_single_base_recovery_uses_displaced_class_name() {
19593        let source = r#"
19594class CORE_EXPORT QgsPoint : public AbstractGeometry
19595{
19596    Q_GADGET
19597
19598    Q_PROPERTY( double x READ x WRITE setX )
19599    Q_PROPERTY( double y READ y WRITE setY )
19600    Q_PROPERTY( double z READ z WRITE setZ )
19601    Q_PROPERTY( double m READ m WRITE setM )
19602
19603  public:
19604#ifndef SIP_RUN
19605    QgsPoint(
19606      double x = std::numeric_limits<double>::quiet_NaN(),
19607      double y = std::numeric_limits<double>::quiet_NaN(),
19608      double z = std::numeric_limits<double>::quiet_NaN(),
19609      double m = std::numeric_limits<double>::quiet_NaN(),
19610      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
19611    );
19612#else
19613    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 )];
19614    % MethodCode
19615    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19616    {
19617      int state;
19618      sipIsErr = 0;
19619      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19620      if ( !sipIsErr )
19621      {
19622        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19623      }
19624      sipReleaseType( p, sipType_QgsPointXY, state );
19625    }
19626    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
19627    {
19628      int state;
19629      sipIsErr = 0;
19630
19631      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
19632      if ( !sipIsErr )
19633      {
19634        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
19635      }
19636      sipReleaseType( p, sipType_QPointF, state );
19637    }
19638    else if (
19639      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
19640      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
19641      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
19642      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
19643    {
19644      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
19645      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
19646      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
19647      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
19648      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
19649      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
19650    }
19651    else // Invalid ctor arguments
19652    {
19653      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
19654      sipIsErr = 1;
19655    }
19656    % End
19657#endif
19658
19659    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
19660    explicit QgsPoint( QPointF p ) SIP_SKIP;
19661    explicit QgsPoint(
19662      Qgis::WkbType wkbType,
19663      double x = std::numeric_limits<double>::quiet_NaN(),
19664      double y = std::numeric_limits<double>::quiet_NaN(),
19665      double z = std::numeric_limits<double>::quiet_NaN(),
19666      double m = std::numeric_limits<double>::quiet_NaN()
19667    ) SIP_SKIP;
19668    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19669    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
19670    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
19671#ifndef SIP_RUN
19672  private:
19673    bool fuzzyHelper(
19674      double epsilon,
19675      const AbstractGeometry &other,
19676      bool is3DFlag,
19677      bool isMeasureFlag
19678    ) const
19679    {
19680      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
19681    }
19682#endif
19683};
19684class Ordinary : public Base { public: Ordinary(); };
19685class API_EXPORT Plain { public: Plain(); };
19686class API_EXPORT : public Base {};
19687class
19688PN_CPP_CLASS_EXTERN Sender : public Link {
19689    Sender();
19690    struct impl;
19691    struct impl& get_impl() const;
19692};
19693class thread_ctx_t {};
19694class ctx_t ZMQ_FINAL : public thread_ctx_t {
19695    bool start();
19696};
19697"#;
19698        let mut parser = tree_sitter::Parser::new();
19699        parser
19700            .set_language(&tree_sitter_cpp::LANGUAGE.into())
19701            .unwrap();
19702        let tree = parser.parse(source, None).unwrap();
19703        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
19704        let parsed = parse_cpp_file(&file, source, &tree);
19705        let declarations = parsed.declarations();
19706
19707        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
19708            assert!(
19709                declarations
19710                    .iter()
19711                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
19712                "missing recovered class {expected}: {declarations:#?}"
19713            );
19714        }
19715        let qgs_point = declarations
19716            .iter()
19717            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
19718            .expect("recovered QgsPoint class");
19719        assert_eq!(
19720            parsed.raw_supertypes.get(qgs_point),
19721            Some(&vec!["AbstractGeometry".to_string()]),
19722            "single-base export recovery must retain its displaced base"
19723        );
19724        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
19725        assert!(
19726            parsed
19727                .navigation_ranges
19728                .get(qgs_point)
19729                .is_some_and(|ranges| {
19730                    !ranges.is_empty()
19731                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
19732                }),
19733            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
19734            parsed.navigation_ranges.get(qgs_point)
19735        );
19736        let sender = declarations
19737            .iter()
19738            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
19739            .expect("recovered Sender class");
19740        assert_eq!(
19741            parsed.raw_supertypes.get(sender),
19742            Some(&vec!["Link".to_string()]),
19743            "post-declarator export recovery must retain its displaced base"
19744        );
19745        let recovered_member = declarations
19746            .iter()
19747            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
19748            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
19749        assert_eq!(
19750            parsed
19751                .signature_metadata
19752                .get(recovered_member)
19753                .and_then(|metadata| metadata.first())
19754                .and_then(SignatureMetadata::callable_linkage),
19755            Some(CallableLinkage::External),
19756            "a named recovered class's members have external linkage"
19757        );
19758        let ctx = declarations
19759            .iter()
19760            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
19761            .expect("recovered ctx_t class");
19762        assert_eq!(
19763            parsed.raw_supertypes.get(ctx),
19764            Some(&vec!["thread_ctx_t".to_string()]),
19765            "postfix export-macro recovery must retain its displaced base"
19766        );
19767        assert!(
19768            declarations.iter().any(|unit| {
19769                unit.is_function()
19770                    && unit.fq_name() == "QgsPoint.QgsPoint"
19771                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
19772            }),
19773            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
19774        );
19775        assert!(
19776            declarations.iter().all(|unit| {
19777                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
19778            }),
19779            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
19780        );
19781    }
19782
19783    #[test]
19784    fn function_like_export_macro_classes_keep_names_and_base_edges() {
19785        // Every sibling shape tree-sitter produces after the `class MACRO(2, 0)`
19786        // error: a plain body, a single base, `final` without a base, `final`
19787        // with one base, and `final` with a comma-separated base list.
19788        let source = r#"
19789namespace api {
19790class PROJECT_PUBLIC_API(2, 0) Prelude {
19791  public:
19792    Prelude();
19793};
19794class PROJECT_PUBLIC_API(2, 0) Base {
19795  public:
19796    Base(int value);
19797};
19798class PROJECT_PUBLIC_API(2, 0) Mixin {
19799  public:
19800    Mixin();
19801};
19802class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
19803  public:
19804    Adopted(int value);
19805};
19806class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
19807  public:
19808    Derived(int value);
19809};
19810class PROJECT_PUBLIC_API(2, 0) Solo final {
19811  public:
19812    Solo();
19813};
19814class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
19815  public:
19816    Blended(int value);
19817};
19818class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
19819  public:
19820    Woven(int value);
19821};
19822} // namespace api
19823"#;
19824        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
19825        let declarations = parsed.declarations();
19826        let class_named = |name: &str| {
19827            declarations
19828                .iter()
19829                .find(|unit| unit.is_class() && unit.fq_name() == name)
19830                .unwrap_or_else(|| {
19831                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19832                })
19833        };
19834        let base = class_named("api.Base");
19835        class_named("api.Prelude");
19836        class_named("api.Mixin");
19837
19838        assert_eq!(
19839            parsed.raw_supertypes.get(class_named("api.Adopted")),
19840            Some(&vec!["Base".to_string()])
19841        );
19842        assert_eq!(
19843            parsed.raw_supertypes.get(class_named("api.Derived")),
19844            Some(&vec!["Base".to_string()])
19845        );
19846        assert_eq!(
19847            parsed.raw_supertypes.get(class_named("api.Solo")),
19848            None,
19849            "a final class without a base list must not invent a supertype"
19850        );
19851        assert_eq!(
19852            parsed.raw_supertypes.get(class_named("api.Blended")),
19853            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19854        );
19855        assert_eq!(
19856            parsed.raw_supertypes.get(class_named("api.Woven")),
19857            Some(&vec!["Base".to_string(), "Mixin".to_string()])
19858        );
19859        assert!(
19860            declarations
19861                .iter()
19862                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
19863            "the export macro must not become a declaration: {declarations:#?}"
19864        );
19865        assert!(
19866            declarations.iter().all(|unit| !matches!(
19867                unit.identifier(),
19868                "final" | "public" | "protected" | "private"
19869            )),
19870            "the head specifiers must not become declarations: {declarations:#?}"
19871        );
19872        assert!(
19873            parsed
19874                .navigation_ranges
19875                .get(base)
19876                .is_some_and(|ranges| !ranges.is_empty()),
19877            "the recovered base must retain a navigable declaration range"
19878        );
19879    }
19880
19881    #[test]
19882    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
19883        // #2557: the class name is the last identifier before the head ends
19884        // (`final`, the base clause `:`, or the body), whatever its spelling.
19885        // A class named in capitals (`X509_CA`) is a class, and an object-like
19886        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
19887        // the control whose spelling never mattered.
19888        let source = r#"
19889namespace api {
19890class PROJECT_PUBLIC_API(2, 0) Base {
19891  public:
19892    Base();
19893};
19894class PROJECT_PUBLIC_API(2, 0) Mixin {
19895  public:
19896    Mixin();
19897};
19898class PROJECT_PUBLIC_API(2, 0) Name {
19899  public:
19900    Name();
19901};
19902class PROJECT_PUBLIC_API(2, 0) X509_CA final {
19903  public:
19904    X509_CA();
19905};
19906class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
19907  public:
19908    HSS_LMS_KEY();
19909};
19910class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
19911  public:
19912    GOST_3410();
19913};
19914class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
19915  public:
19916    PKCS11_RSA();
19917};
19918class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
19919  public:
19920    Plain();
19921};
19922class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
19923  public:
19924    Decorated();
19925};
19926class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
19927  public:
19928    Layered();
19929};
19930} // namespace api
19931"#;
19932        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
19933        let declarations = parsed.declarations();
19934        let class_named = |name: &str| {
19935            declarations
19936                .iter()
19937                .find(|unit| unit.is_class() && unit.fq_name() == name)
19938                .unwrap_or_else(|| {
19939                    panic!("missing function-like export macro class {name}: {declarations:#?}")
19940                })
19941        };
19942        for (name, bases) in [
19943            ("api.Name", None),
19944            ("api.X509_CA", None),
19945            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
19946            ("api.GOST_3410", Some(vec!["Base"])),
19947            ("api.PKCS11_RSA", None),
19948            ("api.Plain", None),
19949            ("api.Decorated", Some(vec!["Base"])),
19950            ("api.Layered", Some(vec!["Base", "Mixin"])),
19951        ] {
19952            let expected =
19953                bases.map(|bases| bases.into_iter().map(str::to_string).collect::<Vec<_>>());
19954            assert_eq!(
19955                parsed.raw_supertypes.get(class_named(name)),
19956                expected.as_ref(),
19957                "{name}"
19958            );
19959        }
19960        assert!(
19961            declarations.iter().all(|unit| !matches!(
19962                unit.identifier(),
19963                "PROJECT_PUBLIC_API"
19964                    | "OTHER_MACRO"
19965                    | "FIRST_MACRO"
19966                    | "SECOND_MACRO"
19967                    | "final"
19968                    | "public"
19969            )),
19970            "macros and head specifiers must not become declarations: {declarations:#?}"
19971        );
19972    }
19973
19974    #[test]
19975    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
19976        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
19977        // head, the body's `{` and its first member in one declaration-scope
19978        // ERROR, scatters the remaining members across the container's
19979        // siblings, and ends the container on the class's own `}`. The class
19980        // was lost and the macro minted a class of its own.
19981        let source = r#"
19982namespace api {
19983
19984/**
19985* Doc comment
19986*/
19987class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
19988   public:
19989      /**
19990      * Construct from a point.
19991      */
19992      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
19993
19994#if defined(PROJECT_HAS_LEGACY_POINT)
19995      /**
19996      * Construct from a legacy point.
19997      */
19998      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
19999#endif
20000
20001      std::string algo_name() const override;
20002
20003      AlgorithmIdentifier algorithm_identifier() const override;
20004};
20005
20006}
20007"#;
20008        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
20009        let declarations = parsed.declarations();
20010        let class = declarations
20011            .iter()
20012            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
20013            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20014        assert_eq!(
20015            parsed.raw_supertypes.get(class),
20016            Some(&vec!["BaseKey".to_string()]),
20017            "the virtual base is the class's base: {declarations:#?}"
20018        );
20019        for member in [
20020            "api.VirtualBased.algo_name",
20021            "api.VirtualBased.algorithm_identifier",
20022        ] {
20023            assert!(
20024                declarations
20025                    .iter()
20026                    .any(|unit| unit.is_function() && unit.fq_name() == member),
20027                "{member} must be owned by the recovered class: {declarations:#?}"
20028            );
20029        }
20030        assert!(
20031            declarations
20032                .iter()
20033                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20034            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
20035        );
20036    }
20037
20038    #[test]
20039    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
20040        // #2924: object-like macro lines before the head demote the `class`
20041        // keyword to a bare identifier and make the macro invocation an
20042        // `init_declarator`, so the head arrives as a `declaration` rather than
20043        // as an ERROR carrying a `class_specifier`. The name is still the last
20044        // identifier before `final`, and `virtual` is a base specifier rather
20045        // than a base.
20046        let source = r#"
20047namespace api {
20048
20049DIAGNOSTIC_PUSH
20050DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
20051
20052class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
20053                                               public virtual api::Inner::Key {
20054   public:
20055      std::string algo_name() const override;
20056};
20057
20058DIAGNOSTIC_POP
20059
20060}
20061"#;
20062        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
20063        let declarations = parsed.declarations();
20064        let class = declarations
20065            .iter()
20066            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
20067            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20068        assert_eq!(
20069            parsed.raw_supertypes.get(class),
20070            Some(&vec![
20071                "api::Outer::Key".to_string(),
20072                "api::Inner::Key".to_string()
20073            ]),
20074            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
20075        );
20076        assert!(
20077            declarations
20078                .iter()
20079                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
20080            "the member is owned by the recovered class: {declarations:#?}"
20081        );
20082        assert!(
20083            declarations
20084                .iter()
20085                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20086            "the macro invocation must not mint a declaration: {declarations:#?}"
20087        );
20088    }
20089
20090    #[test]
20091    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
20092        // The class embedded in a preceding malformed body follows the same
20093        // rule (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration.
20094        let fixture = |head: &str, name: &str| {
20095            format!(
20096                r#"
20097namespace api {{
20098class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
20099   public:
20100      /** Return a descriptive string. */
20101      const char* what() const noexcept override {{ return m_msg.c_str(); }}
20102
20103      /** Return the type of error. */
20104      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
20105
20106      /** Return an associated error code. */
20107      virtual int error_code() const noexcept {{ return 0; }}
20108
20109      /** Avoid throwing the base directly. */
20110      explicit Exception(std::string_view msg);
20111
20112      /** Avoid throwing the base directly. */
20113      Exception(const char* prefix, std::string_view msg);
20114
20115      /** Avoid throwing the base directly. */
20116      Exception(std::string_view msg, const std::exception& e);
20117
20118   private:
20119      std::string m_msg;
20120}};
20121
20122class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
20123   public:
20124      explicit {name}(std::string_view msg);
20125
20126      explicit {name}(std::string_view msg, std::string_view where);
20127
20128      {name}(std::string_view msg, const std::exception& e);
20129
20130      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
20131}};
20132}} // namespace api
20133"#
20134            )
20135        };
20136        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
20137            let source = fixture(head, name);
20138            let mut parser = Parser::new();
20139            parser
20140                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20141                .expect("set C++ grammar");
20142            let tree = parser.parse(&source, None).expect("parse fixture");
20143            let mut embedded = Vec::new();
20144            let mut stack = vec![tree.root_node()];
20145            while let Some(node) = stack.pop() {
20146                embedded.extend(
20147                    recover_embedded_function_like_export_classes(node, &source)
20148                        .into_iter()
20149                        .map(|recovered| (recovered.name, recovered.raw_supertypes)),
20150                );
20151                let mut cursor = node.walk();
20152                stack.extend(node.named_children(&mut cursor));
20153            }
20154            assert!(
20155                embedded.contains(&(name.to_string(), vec!["Exception".to_string()])),
20156                "{head}: embedded recovery must name the class by position: {embedded:#?}\n{}",
20157                tree.root_node().to_sexp()
20158            );
20159            assert!(
20160                embedded
20161                    .iter()
20162                    .all(|(recovered, _)| recovered != "OTHER_MACRO"),
20163                "{head}: the object-like macro is not a class: {embedded:#?}"
20164            );
20165
20166            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
20167            let declarations = parsed.declarations();
20168            let class = declarations
20169                .iter()
20170                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
20171                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
20172            assert_eq!(
20173                parsed.raw_supertypes.get(class),
20174                Some(&vec!["Exception".to_string()]),
20175                "{head}"
20176            );
20177            assert!(
20178                declarations
20179                    .iter()
20180                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
20181                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
20182            );
20183        }
20184    }
20185
20186    #[test]
20187    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
20188        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
20189        // `class MACRO(3, 6) Name final :` in the error, the first base as the
20190        // `type` of the following declaration, and closes that declaration with
20191        // a zero-width `MISSING identifier`. Reading the head by position must
20192        // not take that missing node for the class name: an empty name panics
20193        // at `FqName` construction, which took the Botan corpus replay down
20194        // (#2557). The head's tokens straddle the two nodes, so the positional
20195        // read spans both and recovers the class rather than leaving the
20196        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
20197        let source = r#"
20198namespace api {
20199class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
20200                                                    public virtual Botan::EC_PublicKey {
20201   public:
20202      std::string algo_name() const override { return "ECDSA"; }
20203};
20204} // namespace api
20205"#;
20206        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
20207        let declarations = parsed.declarations();
20208        assert!(
20209            declarations
20210                .iter()
20211                .all(|unit| !unit.identifier().is_empty()),
20212            "no declaration may carry an empty name: {declarations:#?}"
20213        );
20214        assert!(
20215            declarations
20216                .iter()
20217                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
20218            "macros and head specifiers must not become declarations: {declarations:#?}"
20219        );
20220        let class = declarations
20221            .iter()
20222            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
20223            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
20224        assert_eq!(
20225            parsed.raw_supertypes.get(class),
20226            Some(&vec![
20227                "Botan::TPM2::PublicKey".to_string(),
20228                "Botan::EC_PublicKey".to_string()
20229            ]),
20230            "both qualified virtual bases are bases: {declarations:#?}"
20231        );
20232        assert!(
20233            declarations
20234                .iter()
20235                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
20236            "the head must not mint a macro-named class: {declarations:#?}"
20237        );
20238    }
20239
20240    #[test]
20241    fn function_like_export_class_survives_a_preceding_malformed_body() {
20242        let source = r#"
20243namespace api {
20244class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {
20245   public:
20246      /** Return a descriptive string. */
20247      const char* what() const noexcept override { return m_msg.c_str(); }
20248
20249      /** Return the type of error. */
20250      virtual ErrorType error_type() const noexcept { return ErrorType::Unknown; }
20251
20252      /** Return an associated error code. */
20253      virtual int error_code() const noexcept { return 0; }
20254
20255      /** Avoid throwing the base directly. */
20256      explicit Exception(std::string_view msg);
20257
20258      /** Avoid throwing the base directly. */
20259      Exception(const char* prefix, std::string_view msg);
20260
20261      /** Avoid throwing the base directly. */
20262      Exception(std::string_view msg, const std::exception& e);
20263
20264   private:
20265      std::string m_msg;
20266};
20267
20268class PROJECT_PUBLIC_API(2, 0) Invalid_Argument : public Exception {
20269   public:
20270      explicit Invalid_Argument(std::string_view msg);
20271
20272      explicit Invalid_Argument(std::string_view msg, std::string_view where);
20273
20274      Invalid_Argument(std::string_view msg, const std::exception& e);
20275
20276      ErrorType error_type() const noexcept override { return ErrorType::InvalidArgument; }
20277};
20278} // namespace api
20279"#;
20280        let mut parser = Parser::new();
20281        parser
20282            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20283            .expect("set C++ grammar");
20284        let tree = parser.parse(source, None).expect("parse fixture");
20285        let mut stack = vec![tree.root_node()];
20286        let mut saw_embedded_shape = false;
20287        while let Some(node) = stack.pop() {
20288            saw_embedded_shape |= recover_embedded_function_like_export_classes(node, source)
20289                .iter()
20290                .any(|recovered| recovered.name == "Invalid_Argument");
20291            let mut cursor = node.walk();
20292            stack.extend(node.named_children(&mut cursor));
20293        }
20294        assert!(
20295            saw_embedded_shape,
20296            "fixture must retain the embedded error geometry: {}",
20297            tree.root_node().to_sexp()
20298        );
20299
20300        let parsed = parse_cpp_file(
20301            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
20302            source,
20303            &tree,
20304        );
20305        let declarations = parsed.declarations();
20306        let exception = declarations
20307            .iter()
20308            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
20309            .expect("qualified-base export class");
20310        let invalid = declarations
20311            .iter()
20312            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
20313            .expect("class embedded in the preceding malformed body");
20314
20315        assert_eq!(
20316            parsed.raw_supertypes.get(exception),
20317            Some(&vec!["std::exception".to_string()])
20318        );
20319        assert_eq!(
20320            parsed.raw_supertypes.get(invalid),
20321            Some(&vec!["Exception".to_string()])
20322        );
20323        assert!(
20324            parsed.materialization_records.iter().any(|record| matches!(
20325                record,
20326                MaterializationRecord::RecoveredDeclaration { unit, .. }
20327                    if unit == invalid
20328            )),
20329            "the embedded class must retain recovery provenance: {:#?}",
20330            parsed.materialization_records
20331        );
20332    }
20333
20334    #[test]
20335    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
20336        let source = r#"
20337public:
20338   explicit Lookup_Error(std::string_view err) : Exception(err) {}
20339
20340   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
20341"#;
20342        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
20343            .expect("reparse merged constructor body");
20344        let (range, body) =
20345            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
20346                .unwrap_or_else(|| {
20347                    panic!(
20348                        "the merged constructor must retain its structured declarator/body: {}",
20349                        tree.root_node().to_sexp()
20350                    )
20351                });
20352        assert_eq!(
20353            source.get(range).expect("constructor range"),
20354            "Lookup_Error(std::string_view err) : Exception(err) {}"
20355        );
20356        assert_eq!(node_text(body, source), "{}");
20357    }
20358
20359    #[test]
20360    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
20361        let positive_source =
20362            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
20363        let mut parser = tree_sitter::Parser::new();
20364        parser
20365            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20366            .unwrap();
20367        let positive_tree = parser.parse(positive_source, None).unwrap();
20368        assert!(cpp_reparsed_members_are_indexable(
20369            positive_tree.root_node(),
20370            positive_source
20371        ));
20372
20373        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
20374        let negative_tree = parser.parse(negative_source, None).unwrap();
20375        assert!(!cpp_reparsed_members_are_indexable(
20376            negative_tree.root_node(),
20377            negative_source
20378        ));
20379    }
20380
20381    #[test]
20382    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
20383        let copy_control_source = r#"
20384public:
20385    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
20386    explicit Token(const Token* tok);
20387    ~Token();
20388    Token* astOperand1() { return nullptr; }
20389"#;
20390        let constraint_source = r#"
20391private:
20392    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
20393    static T *tokAtImpl(T *tok, int index) {
20394        return tok;
20395    }
20396
20397    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
20398    static T *linkAtImpl(T *tok, int index) {
20399        return tok;
20400    }
20401
20402public:
20403    int late() const { return 1; }
20404"#;
20405        let mut parser = tree_sitter::Parser::new();
20406        parser
20407            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20408            .unwrap();
20409        let copy_control_tree = parser
20410            .parse(copy_control_source, None)
20411            .expect("parse copy-control fixture");
20412        assert!(
20413            copy_control_tree.root_node().has_error(),
20414            "fixture must exercise adjacent copy-control recovery"
20415        );
20416        assert!(
20417            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
20418            "a complete late getter must remain recoverable after adjacent copy-control declarations"
20419        );
20420        let mut cursor = copy_control_tree.root_node().walk();
20421        assert!(
20422            copy_control_tree
20423                .root_node()
20424                .named_children(&mut cursor)
20425                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
20426            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
20427            copy_control_tree.root_node().to_sexp()
20428        );
20429        let constraint_tree = parser
20430            .parse(constraint_source, None)
20431            .expect("parse constraint-macro fixture");
20432        assert!(constraint_tree.root_node().has_error());
20433        assert!(
20434            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
20435            "complete constraint-macro members must not hide a later ordinary member"
20436        );
20437        let mut cursor = constraint_tree.root_node().walk();
20438        assert!(
20439            constraint_tree
20440                .root_node()
20441                .named_children(&mut cursor)
20442                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
20443                    child,
20444                    constraint_source
20445                )),
20446            "fixture must retain the split constraint-macro prefix/function geometry"
20447        );
20448    }
20449
20450    #[test]
20451    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
20452        let source = r#"
20453struct Analyzer {
20454    struct Action {
20455        Action() = default;
20456        Action(const Action&) = default;
20457        Action& operator=(const Action& rhs) & = default;
20458
20459        template<class T,
20460                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
20461                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
20462        // NOLINTNEXTLINE(google-explicit-constructor)
20463        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
20464        {}
20465
20466        enum : std::uint16_t { None = 0, Read = (1 << 0) };
20467        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
20468
20469    private:
20470        unsigned int mFlag{};
20471    };
20472
20473    enum class Direction : unsigned char { Forward, Reverse };
20474    virtual Action analyze(Direction d) const = 0;
20475};
20476"#;
20477        let mut parser = tree_sitter::Parser::new();
20478        parser
20479            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20480            .unwrap();
20481        let tree = parser.parse(source, None).unwrap();
20482        assert!(tree.root_node().has_error());
20483        let root = tree.root_node();
20484        let outer = root
20485            .named_children(&mut root.walk())
20486            .find(|child| child.kind() == "ERROR")
20487            .expect("fragmented Analyzer prefix");
20488        let outer_recovered =
20489            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
20490        assert_eq!(outer_recovered.name, "Analyzer");
20491        let outer_tree = cpp_reparse_fragmented_class_body(
20492            source,
20493            outer_recovered.body.reparse_start,
20494            outer_recovered.body.reparse_end,
20495        )
20496        .expect("reparse Analyzer body");
20497        let outer_root = outer_tree.root_node();
20498        let action_prefix = outer_root
20499            .named_children(&mut outer_root.walk())
20500            .find(|child| child.kind() == "ERROR")
20501            .expect("fragmented Action prefix");
20502        let action_recovered = fragmented_class_body(action_prefix, source)
20503            .expect("structured Action fragment boundary");
20504        assert_eq!(action_recovered.name, "Action");
20505        let action_tree = cpp_reparse_fragmented_class_body(
20506            source,
20507            action_recovered.body.reparse_start,
20508            action_recovered.body.reparse_end,
20509        )
20510        .expect("reparse Action body");
20511        let action_root = action_tree.root_node();
20512        let macro_prefix = action_root
20513            .named_children(&mut action_root.walk())
20514            .find(|child| child.kind() == "ERROR")
20515            .expect("constraint macro prefix");
20516        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
20517            .expect("structured template macro prefix");
20518        let macro_companion =
20519            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
20520        assert!(
20521            cpp_reparsed_template_macro_constructor_companion_is_indexable(
20522                macro_companion,
20523                macro_parameter,
20524                source,
20525            ),
20526            "split constrained constructor must be admitted: {}",
20527            macro_companion.to_sexp()
20528        );
20529        assert!(
20530            cpp_reparsed_members_are_indexable(action_root, source),
20531            "complete Action body must pass the recovery gate: {}",
20532            action_tree.root_node().to_sexp()
20533        );
20534        assert!(
20535            cpp_reparsed_members_are_indexable(outer_root, source),
20536            "complete Analyzer body must pass the recovery gate: {}",
20537            outer_tree.root_node().to_sexp()
20538        );
20539        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
20540        let parsed = parse_cpp_file(&file, source, &tree);
20541        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
20542            assert!(
20543                parsed
20544                    .declarations()
20545                    .iter()
20546                    .any(|unit| unit.fq_name() == expected),
20547                "missing recovered declaration {expected}: {:#?}",
20548                parsed.declarations()
20549            );
20550        }
20551        assert!(
20552            parsed
20553                .declarations()
20554                .iter()
20555                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
20556            "nested members must not remain flattened: {:#?}",
20557            parsed.declarations()
20558        );
20559    }
20560
20561    #[test]
20562    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
20563        let source = r#"
20564raw_hash_set& operator=(raw_hash_set&& that) {
20565  return move_assign(
20566      std::move(that),
20567      typename AllocTraits::propagate_on_container_move_assignment());
20568}
20569
20570iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
20571  return {};
20572}
20573
20574void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
20575
20576iterator insert(const_iterator hint, value_type&& value)
20577    ABSL_ATTRIBUTE_LIFETIME_BOUND {
20578  return {};
20579}
20580
20581friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
20582  return left.size() == right.size();
20583}
20584
20585static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
20586  return static_cast<slot_type*>(buffer);
20587}
20588
20589protected:
20590// Included-range recovery can attach this comment to the template prefix.
20591template <class K>
20592void AssertOnFind([[maybe_unused]] const K& key) {
20593  Check(key);
20594}
20595"#;
20596        let mut parser = tree_sitter::Parser::new();
20597        parser
20598            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20599            .unwrap();
20600        let tree = parser.parse(source, None).unwrap();
20601        assert!(
20602            tree.root_node().has_error(),
20603            "the fixture must exercise tree-sitter's errorful member shapes"
20604        );
20605        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
20606
20607        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
20608        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
20609        assert!(!cpp_reparsed_members_are_indexable(
20610            incomplete_tree.root_node(),
20611            incomplete_source
20612        ));
20613
20614        let outside_error_source = "int foo() stray_attribute {}\n";
20615        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
20616        assert!(outside_error_tree.root_node().has_error());
20617        assert!(!cpp_reparsed_members_are_indexable(
20618            outside_error_tree.root_node(),
20619            outside_error_source
20620        ));
20621
20622        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
20623        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20624        assert!(!cpp_reparsed_members_are_indexable(
20625            variable_initializer_tree.root_node(),
20626            variable_initializer_source
20627        ));
20628    }
20629
20630    #[test]
20631    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
20632        let positive_source = r#"
20633std::pair<iterator, bool> insert(init_type&& value)
20634    ABSL_ATTRIBUTE_LIFETIME_BOUND
20635#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20636  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20637#endif
20638{
20639  return emplace(std::move(value));
20640}
20641"#;
20642        let mut parser = tree_sitter::Parser::new();
20643        parser
20644            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20645            .unwrap();
20646        let positive_tree = parser.parse(positive_source, None).unwrap();
20647        assert!(
20648            positive_tree.root_node().has_error(),
20649            "the fixture must exercise the split attribute/requires shape"
20650        );
20651        assert!(cpp_reparsed_members_are_indexable(
20652            positive_tree.root_node(),
20653            positive_source
20654        ));
20655
20656        let template_return_source = r#"
20657pair<int> insert(init_type&& value)
20658    ABSL_ATTRIBUTE_LIFETIME_BOUND
20659#if LANGUAGE_LEVEL >= 202002L
20660  requires(!Predicate<init_type>::value)
20661#endif
20662// Attributes and the function body may be separated by comments.
20663{
20664  return {};
20665}
20666"#;
20667        let template_return_tree = parser.parse(template_return_source, None).unwrap();
20668        assert!(
20669            cpp_reparsed_members_are_indexable(
20670                template_return_tree.root_node(),
20671                template_return_source
20672            ),
20673            "template-return attribute/requires tree: {}",
20674            template_return_tree.root_node().to_sexp()
20675        );
20676
20677        let no_body_source = r#"
20678std::pair<iterator, bool> insert(init_type&& value)
20679    ABSL_ATTRIBUTE_LIFETIME_BOUND
20680#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
20681  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
20682#endif
20683+ 0;
20684"#;
20685        let no_body_tree = parser.parse(no_body_source, None).unwrap();
20686        assert!(!cpp_reparsed_members_are_indexable(
20687            no_body_tree.root_node(),
20688            no_body_source
20689        ));
20690
20691        let extra_payload_source = r#"
20692pair<int> insert(init_type&& value)
20693    ABSL_ATTRIBUTE_LIFETIME_BOUND
20694#if LANGUAGE_LEVEL >= 202002L
20695  int unrelated;
20696  requires(Predicate<init_type>::value)
20697#endif
20698{
20699  return {};
20700}
20701"#;
20702        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
20703        assert!(!cpp_reparsed_members_are_indexable(
20704            extra_payload_tree.root_node(),
20705            extra_payload_source
20706        ));
20707
20708        let variable_initializer_source = r#"
20709int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
20710#if LANGUAGE_LEVEL >= 202002L
20711  requires(true)
20712#endif
20713{
20714  bad;
20715}
20716"#;
20717        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
20718        assert!(!cpp_reparsed_members_are_indexable(
20719            variable_initializer_tree.root_node(),
20720            variable_initializer_source
20721        ));
20722    }
20723
20724    #[test]
20725    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
20726        let source = r#"namespace absl {
20727ABSL_NAMESPACE_BEGIN namespace container_internal {
20728
20729class raw_hash_set : public Base {
20730 public:
20731  using value_type = int;
20732
20733  template <class U,
20734            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
20735  void insert(U value) { (void)value; }
20736
20737  struct InsertSlot {
20738    raw_hash_set& s;
20739  };
20740};
20741
20742}
20743ABSL_NAMESPACE_END
20744}"#;
20745        let mut parser = tree_sitter::Parser::new();
20746        parser
20747            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20748            .unwrap();
20749        let tree = parser.parse(source, None).unwrap();
20750        let root = tree.root_node();
20751        let outer_namespace = root
20752            .named_children(&mut root.walk())
20753            .find(|child| child.kind() == "namespace_definition")
20754            .expect("outer absl namespace");
20755        let declaration_list = outer_namespace
20756            .child_by_field_name("body")
20757            .expect("outer namespace body");
20758        let sentinel_function = declaration_list
20759            .named_children(&mut declaration_list.walk())
20760            .find(|child| child.kind() == "function_definition")
20761            .expect("malformed namespace sentinel function");
20762        let ancestry = ParentIndex::new(root);
20763        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
20764            .expect("structured nested namespace sentinel");
20765        let fragmented =
20766            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
20767                .expect("fragmented raw_hash_set class");
20768        assert_eq!(fragmented.class_node.kind(), "ERROR");
20769        assert_eq!(fragmented.name, "raw_hash_set");
20770        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
20771
20772        let outer_scope =
20773            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
20774        let mut outer_siblings = Vec::new();
20775        push_cpp_sentinel_sibling_classes(
20776            &mut outer_siblings,
20777            declaration_list,
20778            sentinel.function,
20779            &outer_scope,
20780            source,
20781            &ancestry,
20782        );
20783        let [outer_shadow] = outer_siblings.as_slice() else {
20784            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
20785        };
20786        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
20787        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
20788
20789        let field = "    raw_hash_set& s;";
20790        let start = source.find(field).expect("InsertSlot field") + 4;
20791        let node = root
20792            .descendant_for_byte_range(start, start + "raw_hash_set".len())
20793            .expect("raw_hash_set type node");
20794        let recovered = cpp_sentinel_recovered_classes(root, source);
20795        let [deep_class] = recovered.as_slice() else {
20796            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
20797        };
20798        assert_eq!(
20799            deep_class.namespace_scope_components,
20800            vec!["absl", "container_internal"]
20801        );
20802        assert_eq!(
20803            deep_class.scope_components,
20804            vec!["absl", "container_internal", "raw_hash_set"]
20805        );
20806        assert!(
20807            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
20808                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
20809        );
20810
20811        assert_eq!(
20812            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
20813            Some(vec![
20814                "absl".to_string(),
20815                "container_internal".to_string(),
20816                "raw_hash_set".to_string(),
20817                "InsertSlot".to_string(),
20818            ])
20819        );
20820
20821        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
20822        let parsed = parse_cpp_file(&file, source, &tree);
20823        let raw_hash_set = parsed
20824            .declarations()
20825            .iter()
20826            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
20827            .expect("recovered raw_hash_set class");
20828        assert_eq!(
20829            raw_hash_set.fq_name(),
20830            "absl::container_internal.raw_hash_set",
20831            "the recovered declaration must publish under the deeper sentinel namespace"
20832        );
20833        assert_eq!(
20834            parsed.raw_supertypes.get(raw_hash_set),
20835            Some(&vec!["Base".to_string()]),
20836            "the structured base clause on the fragmented ERROR prefix must survive publication"
20837        );
20838        assert!(
20839            parsed.materialization_records.iter().any(|record| matches!(
20840                record,
20841                MaterializationRecord::RecoveredDeclaration { recovery, unit }
20842                    if unit == raw_hash_set && *recovery == deep_class.class_range
20843            )),
20844            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
20845            parsed.materialization_records
20846        );
20847    }
20848
20849    /// Issue #2358: recording an aggregate definition must not walk the whole
20850    /// file.
20851    ///
20852    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
20853    /// class-like shape that has a body, so the removal step runs once per
20854    /// aggregate. It used to `retain` over `top_level_declarations` and over
20855    /// *every* child list in the file on each of those calls, comparing whole
20856    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
20857    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
20858    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
20859    /// tens of thousands of times over and the C forward differential never
20860    /// finished.
20861    ///
20862    /// A definition the file has not already declared removes nothing, so the
20863    /// honest cost is zero regardless of how many other aggregates surround it.
20864    /// Two sizes an order of magnitude apart pin that the count is not merely
20865    /// small but independent of the file.
20866    ///
20867    /// The declaration walk answers every ancestor question from a
20868    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
20869    /// the root for each one (#2361). Substituting the index is only safe
20870    /// because it answers the identical question, so pin that on the shapes
20871    /// this file's recovery paths care about: anonymous and named aggregates,
20872    /// nested namespaces, templates, macro-displaced declarations and the
20873    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
20874    /// too -- `Node::parent` walks the visible tree, not the named one.
20875    #[test]
20876    fn the_parent_index_answers_what_tree_sitter_answers() {
20877        const SHAPES: [&str; 5] = [
20878            "namespace outer { namespace inner { struct Tag { int field; }; } }",
20879            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
20880            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
20881            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
20882            "class API Broken : public First, public Second {\n  void member();\n",
20883        ];
20884        for source in SHAPES {
20885            let mut parser = tree_sitter::Parser::new();
20886            parser
20887                .set_language(&tree_sitter_cpp::LANGUAGE.into())
20888                .unwrap();
20889            let tree = parser.parse(source, None).unwrap();
20890            let root = tree.root_node();
20891            let ancestry = ParentIndex::new(root);
20892            let mut nodes = 0usize;
20893            let mut stack = vec![root];
20894            while let Some(node) = stack.pop() {
20895                nodes += 1;
20896                assert_eq!(
20897                    node.parent().map(|parent| parent.id()),
20898                    ancestry.parent(node).map(|parent| parent.id()),
20899                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
20900                );
20901                let mut cursor = node.walk();
20902                stack.extend(node.children(&mut cursor));
20903            }
20904            assert!(nodes > 1, "{source:?} produced no tree to compare");
20905        }
20906    }
20907
20908    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
20909    /// index for every ancestor edge. Asking tree-sitter directly makes each
20910    /// edge re-descend from the root, turning declaration extraction on a
20911    /// deeply nested generated header from quadratic output work into a cubic
20912    /// tree walk. Exact query counts pin the route without a machine-dependent
20913    /// wall-clock ceiling.
20914    #[test]
20915    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
20916        const DEPTH: usize = 64;
20917        let mut source = String::new();
20918        for level in 0..DEPTH {
20919            writeln!(source, "namespace n{level} {{").unwrap();
20920        }
20921        source.push_str("int deepest(int value);\n");
20922        for _ in 0..DEPTH {
20923            source.push_str("}\n");
20924        }
20925
20926        let mut parser = tree_sitter::Parser::new();
20927        parser
20928            .set_language(&tree_sitter_cpp::LANGUAGE.into())
20929            .unwrap();
20930        let tree = parser.parse(&source, None).unwrap();
20931        let root = tree.root_node();
20932        let ancestry = ParentIndex::new(root);
20933        let mut function_declarator = None;
20934        walk_named_tree_preorder(root, true, |node| {
20935            if node.kind() == "function_declarator" {
20936                function_declarator = Some(node);
20937                WalkControl::Break
20938            } else {
20939                WalkControl::Continue
20940            }
20941        });
20942        let function_declarator = function_declarator.expect("deepest function declarator");
20943        let ancestor_count =
20944            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
20945
20946        ancestry.reset_parent_query_count_for_test();
20947        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
20948        assert_eq!(DEPTH, lexical_scope.len());
20949        assert_eq!(
20950            ancestor_count + 1,
20951            ancestry.parent_query_count_for_test(),
20952            "lexical-scope ancestry bypassed the parent index"
20953        );
20954
20955        ancestry.reset_parent_query_count_for_test();
20956        assert_eq!(
20957            Some(DispatchExtensibility::Closed),
20958            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
20959        );
20960        assert_eq!(
20961            ancestor_count,
20962            ancestry.parent_query_count_for_test(),
20963            "dispatch ancestry bypassed the parent index"
20964        );
20965
20966        ancestry.reset_parent_query_count_for_test();
20967        assert_eq!(
20968            CallableLinkage::External,
20969            cpp_callable_linkage(function_declarator, &source, &ancestry)
20970        );
20971        assert_eq!(
20972            ancestor_count + 1,
20973            ancestry.parent_query_count_for_test(),
20974            "linkage ancestry bypassed the parent index"
20975        );
20976
20977        ancestry.reset_parent_query_count_for_test();
20978        assert!(!cpp_callable_is_structural_constructor(
20979            function_declarator,
20980            &source,
20981            &ancestry
20982        ));
20983        assert_eq!(
20984            ancestor_count + 1,
20985            ancestry.parent_query_count_for_test(),
20986            "constructor ancestry bypassed the parent index"
20987        );
20988    }
20989
20990    /// #3507: the one row-level dispatch fact C++ can state is stated only
20991    /// where the language states it -- in the class body.
20992    ///
20993    /// A call's exactness is not readable from a definition row: an
20994    /// out-of-line definition repeats no `virtual`, no `final`, and no base
20995    /// clause, and C++ inherits virtual-ness from a base that declares the
20996    /// member. Each shape below therefore has to answer for itself, and only
20997    /// a class body with no base clause at all can close a member that writes
20998    /// nothing.
20999    #[test]
21000    fn callable_dispatch_extensibility_comes_from_the_class_body() {
21001        const SOURCE: &str = "\
21002class Plain { public: long send(long order); };
21003class Derived : public Base { public: long send(long order); };
21004class Sealed : public Base { public: long send(long order) final; };
21005class Hiding : public Base { public: long send(long order) { return order; } };
21006class Virtual { public: virtual long send(long order); };
21007namespace shop { long free_call(long order); }
21008long Plain::send(long order) { return order; }
21009";
21010        let mut parser = tree_sitter::Parser::new();
21011        parser
21012            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21013            .unwrap();
21014        let tree = parser.parse(SOURCE, None).unwrap();
21015        let root = tree.root_node();
21016        let ancestry = ParentIndex::new(root);
21017        let mut answers = Vec::new();
21018        walk_named_tree_preorder(root, true, |node| {
21019            if node.kind() != "function_declarator" {
21020                return WalkControl::Continue;
21021            }
21022            let name = cpp_function_declarator_name_node(node).expect("declarator name");
21023            answers.push((
21024                node_text(name, SOURCE).to_string(),
21025                cpp_callable_dispatch_extensibility(node, &ancestry),
21026            ));
21027            WalkControl::Continue
21028        });
21029        assert_eq!(
21030            answers,
21031            vec![
21032                ("send".to_string(), Some(DispatchExtensibility::Closed)),
21033                ("send".to_string(), Some(DispatchExtensibility::Open)),
21034                ("send".to_string(), Some(DispatchExtensibility::Closed)),
21035                ("send".to_string(), Some(DispatchExtensibility::Open)),
21036                ("send".to_string(), Some(DispatchExtensibility::Open)),
21037                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
21038                ("Plain::send".to_string(), None),
21039            ],
21040            "the class body states the fact; a qualified definition states none"
21041        );
21042    }
21043
21044    /// Forward declarations followed by definitions are compacted as one
21045    /// batch, without rescanning the shared namespace/top-level lists for each
21046    /// tag. Definitions are intentionally visited in reverse order so the
21047    /// assertion also pins eager remove-and-reappend ordering.
21048    #[test]
21049    fn forward_declared_aggregates_are_replaced_without_sibling_scans() {
21050        for aggregates in [64usize, 512] {
21051            let mut source =
21052                String::from("typedef unsigned long long u64;\nnamespace generated {\n");
21053            for index in 0..aggregates {
21054                writeln!(source, "struct tag{index};").unwrap();
21055            }
21056            for index in (0..aggregates).rev() {
21057                writeln!(
21058                    source,
21059                    "struct tag{index} {{\n\tu64 first;\n\tint second;\n}};"
21060                )
21061                .unwrap();
21062            }
21063            source.push_str("}\n");
21064
21065            start_code_unit_removal_scan_probe();
21066            let parsed = parse_cpp_declarations(&source, "vmlinux.h");
21067            let scanned = finish_code_unit_removal_scan_probe();
21068
21069            let expected_names: Vec<String> = (0..aggregates)
21070                .rev()
21071                .map(|index| format!("tag{index}"))
21072                .collect();
21073            let top_level_names: Vec<String> = parsed
21074                .top_level_declarations
21075                .iter()
21076                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21077                .map(|unit| unit.short_name().to_string())
21078                .collect();
21079            let namespace = parsed
21080                .declarations()
21081                .iter()
21082                .find(|unit| {
21083                    unit.kind() == CodeUnitType::Module && unit.short_name() == "generated"
21084                })
21085                .expect("generated namespace should be declared");
21086            let child_names: Vec<String> = parsed.children[namespace]
21087                .iter()
21088                .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21089                .map(|unit| unit.short_name().to_string())
21090                .collect();
21091            assert_eq!(
21092                aggregates,
21093                parsed
21094                    .declarations()
21095                    .iter()
21096                    .filter(|unit| unit.is_class() && unit.short_name().starts_with("tag"))
21097                    .count(),
21098                "every aggregate must still be declared at {aggregates} aggregates"
21099            );
21100            assert_eq!(expected_names, top_level_names);
21101            assert_eq!(expected_names, child_names);
21102            assert_eq!(
21103                0, scanned,
21104                "replacing {aggregates} forward declarations must compact their shared lists once"
21105            );
21106        }
21107    }
21108
21109    #[test]
21110    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
21111        const DISTINCT_PER_KIND: usize = 64;
21112        let mut source = String::new();
21113        for index in 0..DISTINCT_PER_KIND {
21114            writeln!(source, "typedef int Alias{index};").unwrap();
21115        }
21116        writeln!(source, "typedef long Alias0;").unwrap();
21117        for index in 0..DISTINCT_PER_KIND {
21118            writeln!(source, "#define MACRO_{index} {index}").unwrap();
21119        }
21120        writeln!(source, "#define MACRO_0 duplicate").unwrap();
21121        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
21122
21123        let mut parser = tree_sitter::Parser::new();
21124        parser
21125            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21126            .unwrap();
21127        let tree = parser.parse(&source, None).unwrap();
21128        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
21129
21130        start_declaration_identity_comparison_probe();
21131        let parsed = parse_cpp_file(&file, &source, &tree);
21132        let comparisons = finish_declaration_identity_comparison_probe();
21133
21134        assert_eq!(
21135            DISTINCT_PER_KIND + 1,
21136            parsed
21137                .declarations()
21138                .iter()
21139                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
21140                .count(),
21141            "every physical typedef alias declaration must be retained so \
21142             conditional branch guards stay available to the resolver"
21143        );
21144        assert_eq!(
21145            DISTINCT_PER_KIND + 1,
21146            parsed
21147                .declarations()
21148                .iter()
21149                .filter(|unit| {
21150                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
21151                })
21152                .count(),
21153            "distinct macro redefinitions must remain available to temporal lookup"
21154        );
21155        assert_eq!(
21156            2,
21157            parsed
21158                .declarations()
21159                .iter()
21160                .filter(|unit| {
21161                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
21162                })
21163                .count(),
21164            "function overloads must remain distinct"
21165        );
21166
21167        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
21168        assert!(
21169            comparisons <= dedup_inputs * 4,
21170            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
21171        );
21172    }
21173
21174    #[test]
21175    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
21176        let source = r#"namespace absl {
21177ABSL_NAMESPACE_BEGIN namespace container_internal {
21178template <typename T>
21179class broken {
21180 public:
21181  using value_type = T;
21182  T operator->() const { return &operator*(); }
21183  using alias = value_type;
21184};
21185}
21186}
21187"#;
21188        let mut parser = tree_sitter::Parser::new();
21189        parser
21190            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21191            .unwrap();
21192        let tree = parser.parse(source, None).unwrap();
21193        let broken = find_class_named(tree.root_node(), source, "broken")
21194            .expect("the positive fixture must expose the broken class node");
21195        assert!(
21196            broken.has_error(),
21197            "the positive fixture must retain an internal parser error"
21198        );
21199        assert!(
21200            cpp_complete_class_body_close(broken).is_some(),
21201            "the positive fixture must expose a real class body close"
21202        );
21203        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21204        assert!(
21205            recovered.iter().any(|class| {
21206                class.scope_components == ["absl", "container_internal", "broken"]
21207            }),
21208            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
21209        );
21210    }
21211
21212    #[test]
21213    fn sentinel_recovery_keeps_members_after_nested_body_close() {
21214        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
21215NLOHMANN_BASIC_JSON_TPL_DECLARATION
21216class basic_json {
21217 private:
21218  union storage {
21219    int value;
21220  } data;
21221 public:
21222  using late_alias = int;
21223  late_alias value() const;
21224};
21225NLOHMANN_JSON_NAMESPACE_END
21226"#;
21227        let mut parser = tree_sitter::Parser::new();
21228        parser
21229            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21230            .unwrap();
21231        let tree = parser.parse(source, None).unwrap();
21232        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21233        let basic_json = recovered
21234            .iter()
21235            .find(|class| {
21236                class
21237                    .scope_components
21238                    .last()
21239                    .is_some_and(|name| name == "basic_json")
21240            })
21241            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
21242        let late_alias = source
21243            .find("late_alias value")
21244            .expect("late alias reference");
21245        assert!(
21246            basic_json.class_range.start_byte < late_alias
21247                && late_alias < basic_json.class_range.end_byte,
21248            "the recovered class range must include members after a nested close: {basic_json:#?}"
21249        );
21250    }
21251
21252    #[test]
21253    fn sentinel_recovery_rejects_class_that_borrows_outer_close() {
21254        let source = r#"namespace absl {
21255ABSL_NAMESPACE_BEGIN namespace container_internal {
21256template <typename T>
21257class broken {
21258 public:
21259  using value_type = T;
21260  T operator->() const { return &operator*(); }
21261}
21262}
21263"#;
21264        let mut parser = tree_sitter::Parser::new();
21265        parser
21266            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21267            .unwrap();
21268        let tree = parser.parse(source, None).unwrap();
21269        let broken = find_class_named(tree.root_node(), source, "broken")
21270            .expect("the negative fixture must expose the malformed class node");
21271        assert!(
21272            broken.has_error(),
21273            "the negative fixture must retain a parser error"
21274        );
21275        assert!(
21276            cpp_complete_class_body_close(broken).is_none(),
21277            "the malformed class must not expose a real body close"
21278        );
21279        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21280        assert!(
21281            recovered
21282                .iter()
21283                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
21284            "an incomplete class must not borrow the namespace close: {recovered:#?}"
21285        );
21286    }
21287
21288    #[test]
21289    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
21290        let source = r#"namespace absl {
21291ABSL_NAMESPACE_BEGIN namespace container_internal {
21292template <typename T>
21293struct broken {
21294  using value_type = T;
21295};
21296}
21297
21298#ifdef OWNER_DEF
21299template <typename T>
21300typename broken<T>::value_type broken<T>::method() {
21301  value_type value{};
21302  return value;
21303}
21304#endif
21305
21306namespace sibling {
21307template <typename T>
21308typename broken<T>::value_type broken<T>::other() {
21309  value_type value{};
21310  return value;
21311}
21312}
21313
21314ABSL_NAMESPACE_END
21315}
21316"#;
21317        let mut parser = tree_sitter::Parser::new();
21318        parser
21319            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21320            .unwrap();
21321        let tree = parser.parse(source, None).unwrap();
21322        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21323        let broken = recovered
21324            .iter()
21325            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
21326            .expect("the sentinel class must be recovered");
21327        let method_start = source
21328            .find("typename broken<T>::value_type broken<T>::method()")
21329            .expect("guarded sibling owner");
21330        let method_end = source[method_start..]
21331            .find("\n}")
21332            .map(|offset| method_start + offset + 2)
21333            .expect("guarded sibling owner close");
21334        assert!(
21335            broken
21336                .owner_ranges
21337                .iter()
21338                .any(|owner| owner.range.start_byte <= method_start
21339                    && method_end <= owner.range.end_byte),
21340            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
21341        );
21342        let sibling_start = source
21343            .find("typename broken<T>::value_type broken<T>::other()")
21344            .expect("nested namespace sibling owner");
21345        assert!(
21346            broken
21347                .owner_ranges
21348                .iter()
21349                .all(|owner| owner.range.start_byte > sibling_start
21350                    || owner.range.end_byte <= sibling_start),
21351            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
21352        );
21353    }
21354
21355    #[test]
21356    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
21357        let source = r#"#ifdef OUTER
21358namespace absl {
21359ABSL_NAMESPACE_BEGIN namespace container_internal {
21360template <typename T>
21361struct broken {
21362  using value_type = T;
21363};
21364}
21365}
21366
21367#ifdef OWNER_DEF
21368template <typename T>
21369typename broken<T>::value_type broken<T>::method() {
21370  value_type value{};
21371  return value;
21372}
21373#endif
21374#endif
21375"#;
21376        let mut parser = tree_sitter::Parser::new();
21377        parser
21378            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21379            .unwrap();
21380        let tree = parser.parse(source, None).unwrap();
21381        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
21382        let broken = recovered
21383            .iter()
21384            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
21385            .expect("the sentinel class must be recovered");
21386        let method_start = source
21387            .find("typename broken<T>::value_type broken<T>::method()")
21388            .expect("outer sibling owner");
21389        assert!(
21390            broken
21391                .owner_ranges
21392                .iter()
21393                .all(|owner| owner.range.start_byte > method_start
21394                    || owner.range.end_byte <= method_start),
21395            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {broken:#?}"
21396        );
21397    }
21398
21399    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
21400    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
21401        let mut signatures = parsed
21402            .declarations()
21403            .iter()
21404            .filter(|unit| unit.is_function() && unit.fq_name() == fq_name)
21405            .filter_map(|unit| unit.signature().map(str::to_string))
21406            .collect::<Vec<_>>();
21407        signatures.sort();
21408        signatures.dedup();
21409        signatures
21410    }
21411
21412    #[test]
21413    fn callable_parameter_types_come_from_the_ast_parameter_list() {
21414        let source = r#"
21415template <typename T, ENABLE_BYTES(T)>
21416Vec256<T> DupOdd(Vec256<T> value) { return value; }
21417
21418struct Visitor {
21419  void fail(this auto const& self) {}
21420};
21421"#;
21422        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
21423        let dup_odd = parsed
21424            .declarations()
21425            .iter()
21426            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
21427            .expect("DupOdd declaration");
21428        assert_eq!(
21429            dup_odd.signature(),
21430            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
21431        );
21432        assert_eq!(
21433            parsed
21434                .signature_metadata
21435                .get(dup_odd)
21436                .and_then(|metadata| metadata.first())
21437                .and_then(SignatureMetadata::callable_parameter_types),
21438            Some(["Vec256<T>".to_string()].as_slice())
21439        );
21440
21441        let fail = parsed
21442            .declarations()
21443            .iter()
21444            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
21445            .expect("explicit-object member");
21446        assert_eq!(fail.signature(), Some("(const this auto &)"));
21447        let metadata = parsed
21448            .signature_metadata
21449            .get(fail)
21450            .and_then(|metadata| metadata.first())
21451            .expect("explicit-object signature metadata");
21452        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
21453        assert!(
21454            metadata
21455                .callable_arity()
21456                .is_some_and(|arity| arity.accepts(0))
21457        );
21458    }
21459
21460    #[test]
21461    fn trailing_qualifiers_survive_parameter_list_whitespace() {
21462        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
21463        // declarator's structure, so an out-of-line definition that spells its
21464        // parameter list with different whitespace than the declaration must
21465        // still carry it.
21466        let source = r#"
21467struct Widget {
21468  bool multiline(int settings, int supprs) const;
21469  bool doublespace(int settings, int supprs) const;
21470  bool noexcept_multiline(int settings, int supprs) noexcept;
21471  bool ref_multiline(int settings, int supprs) &&;
21472};
21473bool
21474Widget::multiline (int settings,
21475                   int supprs) const
21476{ return settings + supprs > 0; }
21477bool Widget::doublespace(int settings,  int supprs) const { return true; }
21478bool Widget::noexcept_multiline(int settings,
21479                                int supprs) noexcept { return true; }
21480bool Widget::ref_multiline(int settings,
21481                           int supprs) && { return true; }
21482"#;
21483        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
21484        assert_eq!(
21485            vec!["(int, int) const".to_string()],
21486            identity_signatures(&parsed, "Widget.multiline")
21487        );
21488        assert_eq!(
21489            vec!["(int, int) const".to_string()],
21490            identity_signatures(&parsed, "Widget.doublespace")
21491        );
21492        assert_eq!(
21493            vec!["(int, int) noexcept".to_string()],
21494            identity_signatures(&parsed, "Widget.noexcept_multiline")
21495        );
21496        assert_eq!(
21497            vec!["(int, int) &&".to_string()],
21498            identity_signatures(&parsed, "Widget.ref_multiline")
21499        );
21500    }
21501
21502    #[test]
21503    fn macro_fragmented_plain_class_keeps_following_member_signature() {
21504        let source = r#"
21505struct CString {};
21506class CMessage {
21507public:
21508  CString GetParams(unsigned int index, unsigned int length = -1) const
21509      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
21510    return GetParamsColon(index, length);
21511  }
21512  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
21513};
21514CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
21515  return {};
21516}
21517"#;
21518        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
21519        assert_eq!(
21520            vec!["(unsigned int, unsigned int) const".to_string()],
21521            identity_signatures(&parsed, "CMessage.GetParamsColon")
21522        );
21523    }
21524
21525    #[test]
21526    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
21527        let source = r#"
21528#pragma once
21529#define DEMO_DEPRECATED(message)
21530namespace demo {
21531struct Base {
21532    static int aligned(int value) { return value; }
21533    int legacy(int value) const
21534        DEMO_DEPRECATED("use replacement()") { return value; }
21535    int replacement() const;
21536    void run(int value);
21537};
21538struct OtherBase {
21539    void run(int value);
21540    static int aligned(int value) { return value; }
21541};
21542struct Derived : Base {};
21543struct Override : Base {
21544    void run(int value);
21545    static int aligned(int value) { return value; }
21546};
21547struct RecoveredOverride : Base {
21548    int legacy(int value) const
21549        DEMO_DEPRECATED("use replacement()") { return value; }
21550    void run(int value);
21551};
21552struct Hidden : Base {
21553    void run(int first, int second);
21554    static int aligned(int first, int second) { return first + second; }
21555};
21556struct Ambiguous : Base, OtherBase {};
21557}
21558struct Global {};
21559"#;
21560        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
21561        let declarations = parsed.declarations();
21562        let fq_names = declarations
21563            .iter()
21564            .map(|unit| unit.fq_name())
21565            .collect::<std::collections::BTreeSet<_>>();
21566
21567        for expected in [
21568            "demo.Base",
21569            "demo.Base.aligned",
21570            "demo.Base.legacy",
21571            "demo.Base.replacement",
21572            "demo.Base.run",
21573            "demo.Derived",
21574            "demo.OtherBase",
21575            "demo.Override",
21576            "demo.RecoveredOverride",
21577            "demo.Hidden",
21578            "demo.Ambiguous",
21579            "Global",
21580        ] {
21581            assert!(
21582                fq_names.contains(expected),
21583                "missing {expected} from namespaced macro fragment: {declarations:#?}"
21584            );
21585        }
21586        assert!(
21587            !fq_names.contains("Derived"),
21588            "following class escaped its namespace: {declarations:#?}"
21589        );
21590        assert!(
21591            !fq_names.contains("demo.Global"),
21592            "global class crossed the recovered namespace boundary: {declarations:#?}"
21593        );
21594    }
21595
21596    #[test]
21597    fn trailing_qualifiers_still_separate_genuine_overloads() {
21598        // The qualifier must keep distinguishing the real C++ overload sets it
21599        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
21600        let source = r#"
21601struct Widget {
21602  int* slot(int index);
21603  const int* slot(int index) const;
21604  int log(int severity) &;
21605  int log(int severity) &&;
21606};
21607"#;
21608        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
21609        assert_eq!(
21610            vec!["(int)".to_string(), "(int) const".to_string()],
21611            identity_signatures(&parsed, "Widget.slot")
21612        );
21613        assert_eq!(
21614            vec!["(int) &".to_string(), "(int) &&".to_string()],
21615            identity_signatures(&parsed, "Widget.log")
21616        );
21617    }
21618
21619    #[test]
21620    fn virtual_specifier_is_not_part_of_the_identity_signature() {
21621        // `override` never appears on the out-of-line definition, and C++ does
21622        // not make it part of the signature, so it must not split the identity.
21623        let source = r#"
21624struct Base {
21625  virtual void run(int value) const;
21626};
21627struct Widget : Base {
21628  void run(int value) const override;
21629};
21630void Widget::run(int value) const {}
21631"#;
21632        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
21633        assert_eq!(
21634            vec!["(int) const".to_string()],
21635            identity_signatures(&parsed, "Widget.run")
21636        );
21637    }
21638
21639    #[test]
21640    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
21641        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
21642        // the function type, so a declaration that spells `const int` and a
21643        // definition that spells `int` are one entity.
21644        let source = r#"
21645struct Widget {
21646  bool value_params(const int settings, const int supprs);
21647  void pointee_const(const int* p);
21648  void pointer_const(int* const p);
21649  void both_const(const int* const p);
21650  void reference_const(const int& p);
21651  void array_const(const int values[4]);
21652};
21653bool Widget::value_params(int settings, int supprs) { return true; }
21654void Widget::pointer_const(int* p) {}
21655void Widget::both_const(const int* p) {}
21656"#;
21657        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
21658        assert_eq!(
21659            vec!["(int, int)".to_string()],
21660            identity_signatures(&parsed, "Widget.value_params")
21661        );
21662        assert_eq!(
21663            vec!["(int *)".to_string()],
21664            identity_signatures(&parsed, "Widget.pointer_const")
21665        );
21666        assert_eq!(
21667            vec!["(const int *)".to_string()],
21668            identity_signatures(&parsed, "Widget.both_const")
21669        );
21670        // The const that is not top-level still distinguishes the type.
21671        assert_eq!(
21672            vec!["(const int *)".to_string()],
21673            identity_signatures(&parsed, "Widget.pointee_const")
21674        );
21675        assert_eq!(
21676            vec!["(const int &)".to_string()],
21677            identity_signatures(&parsed, "Widget.reference_const")
21678        );
21679        assert_eq!(
21680            vec!["(const int [4])".to_string()],
21681            identity_signatures(&parsed, "Widget.array_const")
21682        );
21683    }
21684
21685    #[test]
21686    fn top_level_parameter_const_still_separates_pointee_overloads() {
21687        let source = r#"
21688struct Widget {
21689  void take(const int* p);
21690  void take(int* p);
21691};
21692"#;
21693        let parsed = parse_cpp_declarations(source, "pointee-overloads.cpp");
21694        assert_eq!(
21695            vec!["(const int *)".to_string(), "(int *)".to_string()],
21696            identity_signatures(&parsed, "Widget.take")
21697        );
21698    }
21699
21700    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
21701        let mut parser = tree_sitter::Parser::new();
21702        parser
21703            .set_language(&tree_sitter_cpp::LANGUAGE.into())
21704            .unwrap();
21705        let tree = parser.parse(source, None).unwrap();
21706        let start = source.find(callable_name).expect("callable declaration");
21707        let declarator =
21708            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
21709        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
21710    }
21711
21712    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
21713        let mut shapes = comparable_shapes(source, callable_name);
21714        assert_eq!(1, shapes.len(), "{shapes:?}");
21715        match shapes.remove(0) {
21716            CppComparableSlot::Shape(shape) => shape,
21717            other => panic!("expected a comparable shape, got {other:?}"),
21718        }
21719    }
21720
21721    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
21722        let mut current = shape.root();
21723        loop {
21724            match shape.node(current) {
21725                CppComparableNode::Named { .. } => return shape.node(current),
21726                CppComparableNode::Pointer { inner, .. }
21727                | CppComparableNode::Reference { inner }
21728                | CppComparableNode::Array { inner } => current = *inner,
21729                CppComparableNode::Generic { base, .. } => current = *base,
21730            }
21731        }
21732    }
21733
21734    #[test]
21735    fn comparable_shape_keeps_pointee_const() {
21736        assert_ne!(
21737            sole_comparable_shape("void f(const char* p);", "f("),
21738            sole_comparable_shape("void f(char* p);", "f(")
21739        );
21740    }
21741
21742    #[test]
21743    fn comparable_shape_keeps_inner_pointer_const() {
21744        assert_ne!(
21745            sole_comparable_shape("void f(int** p);", "f("),
21746            sole_comparable_shape("void f(int* const* p);", "f(")
21747        );
21748    }
21749
21750    #[test]
21751    fn comparable_shape_drops_top_level_pointer_const() {
21752        assert_eq!(
21753            sole_comparable_shape("void f(int* const p);", "f("),
21754            sole_comparable_shape("void f(int* p);", "f(")
21755        );
21756    }
21757
21758    #[test]
21759    fn comparable_shape_drops_top_level_base_const() {
21760        assert_eq!(
21761            sole_comparable_shape("void f(const int p);", "f("),
21762            sole_comparable_shape("void f(int p);", "f(")
21763        );
21764    }
21765
21766    #[test]
21767    fn comparable_shape_decays_top_level_array_to_pointer() {
21768        assert_eq!(
21769            sole_comparable_shape("void f(int a[3]);", "f("),
21770            sole_comparable_shape("void f(int* a);", "f(")
21771        );
21772        assert_eq!(
21773            sole_comparable_shape("void f(int* a[3]);", "f("),
21774            sole_comparable_shape("void f(int** a);", "f(")
21775        );
21776    }
21777
21778    #[test]
21779    fn comparable_shape_keeps_array_behind_pointer() {
21780        assert_ne!(
21781            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
21782            sole_comparable_shape("struct S { void f(int** a); };", "f(")
21783        );
21784    }
21785
21786    #[test]
21787    fn comparable_shape_records_written_name_and_lexical_scope() {
21788        let declared =
21789            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
21790        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
21791        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
21792            panic!("named leaf");
21793        };
21794        assert_eq!(["Msg".to_string()].as_slice(), name.path());
21795        assert_eq!(
21796            ["ns".to_string(), "S".to_string()].as_slice(),
21797            name.lexical_scope()
21798        );
21799        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
21800            panic!("named leaf");
21801        };
21802        assert_eq!(
21803            ["ns".to_string(), "Msg".to_string()].as_slice(),
21804            name.path()
21805        );
21806        assert!(name.lexical_scope().is_empty());
21807        assert_ne!(declared, defined);
21808    }
21809
21810    #[test]
21811    fn comparable_shape_marks_sized_primitive_leaf() {
21812        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
21813        let CppComparableNode::Named {
21814            name, primitive, ..
21815        } = comparable_named_leaf(&shape)
21816        else {
21817            panic!("named leaf");
21818        };
21819        assert!(primitive);
21820        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
21821        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
21822    }
21823
21824    #[test]
21825    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
21826        assert_eq!(
21827            vec![CppComparableSlot::Unstructured],
21828            comparable_shapes("void f(void (*cb)(int));", "f(")
21829        );
21830    }
21831
21832    #[test]
21833    fn comparable_shape_reports_ellipsis_slot() {
21834        let shapes = comparable_shapes("void f(int a, ...);", "f(");
21835        assert_eq!(2, shapes.len(), "{shapes:?}");
21836        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
21837    }
21838
21839    #[test]
21840    fn comparable_shape_keeps_template_argument_const() {
21841        assert_ne!(
21842            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
21843            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
21844        );
21845    }
21846
21847    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
21848    /// file-scope tag that a later `struct inner *` at file scope may name.
21849    #[test]
21850    fn c_file_mints_aggregate_member_tag_at_file_scope() {
21851        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21852        let parsed = parse_cpp_declarations(source, "x.c");
21853        let declarations = parsed.declarations();
21854
21855        assert!(
21856            declarations
21857                .iter()
21858                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
21859            "expected a file-scope inner tag, got {declarations:?}"
21860        );
21861        assert!(
21862            declarations
21863                .iter()
21864                .all(|unit| unit.fq_name() != "outer$inner"),
21865            "expected no nested identity, got {declarations:?}"
21866        );
21867        assert!(
21868            declarations
21869                .iter()
21870                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
21871        );
21872        // Members still belong to their own aggregate.
21873        assert!(
21874            declarations
21875                .iter()
21876                .any(|unit| unit.fq_name() == "inner.value")
21877        );
21878        assert!(
21879            declarations
21880                .iter()
21881                .any(|unit| unit.fq_name() == "outer.item")
21882        );
21883
21884        let outer = declarations
21885            .iter()
21886            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
21887            .expect("outer");
21888        assert!(
21889            parsed
21890                .children
21891                .get(outer)
21892                .into_iter()
21893                .flatten()
21894                .all(|child| child.fq_name() != "inner"),
21895            "the tag must not hang off the aggregate it is written inside: {:?}",
21896            parsed.children
21897        );
21898    }
21899
21900    /// A header carries no compilation language of its own, and a `.cpp`
21901    /// translation unit really does declare a nested class. Both keep exactly
21902    /// the C++ extraction they had before the C dialect existed.
21903    #[test]
21904    fn header_and_cpp_files_keep_nested_tag_identity() {
21905        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21906        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
21907            let parsed = parse_cpp_declarations(source, name);
21908            let declarations = parsed.declarations();
21909            assert!(
21910                declarations
21911                    .iter()
21912                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
21913                "{name} must keep the nested identity, got {declarations:?}"
21914            );
21915            assert!(
21916                declarations.iter().all(|unit| unit.fq_name() != "inner"),
21917                "{name} must not mint a file-scope tag, got {declarations:?}"
21918            );
21919            assert!(
21920                declarations
21921                    .iter()
21922                    .any(|unit| unit.fq_name() == "outer$inner.value")
21923            );
21924        }
21925    }
21926
21927    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
21928    #[test]
21929    fn uppercase_c_extension_keeps_cpp_tag_scope() {
21930        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
21931        let parsed = parse_cpp_declarations(source, "x.C");
21932        assert!(
21933            parsed
21934                .declarations()
21935                .iter()
21936                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
21937        );
21938    }
21939
21940    /// There is no such thing as a partially nested tag in C: every level of a
21941    /// nested aggregate chain lands at the same enclosing scope.
21942    #[test]
21943    fn c_file_mints_every_nesting_level_at_file_scope() {
21944        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
21945        let parsed = parse_cpp_declarations(source, "z.c");
21946        let declarations = parsed.declarations();
21947
21948        for tag in ["a", "b", "c"] {
21949            assert!(
21950                declarations
21951                    .iter()
21952                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
21953                "expected a file-scope {tag}, got {declarations:?}"
21954            );
21955        }
21956        assert!(
21957            declarations
21958                .iter()
21959                .all(|unit| !unit.fq_name().contains('$')),
21960            "no level may keep a nested identity, got {declarations:?}"
21961        );
21962        // Each member still belongs to the aggregate that declares it.
21963        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
21964        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
21965        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
21966    }
21967
21968    /// An enum tag is a tag; its enumerators stay members of the enum, which is
21969    /// what makes them ordinary identifiers at the enum's own (file) scope.
21970    #[test]
21971    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
21972        let source = "struct outer { enum color { RED, GREEN } c; };\n";
21973        let parsed = parse_cpp_declarations(source, "e.c");
21974        let declarations = parsed.declarations();
21975
21976        let color = declarations
21977            .iter()
21978            .find(|unit| unit.is_class() && unit.fq_name() == "color")
21979            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
21980        assert!(
21981            declarations
21982                .iter()
21983                .all(|unit| unit.fq_name() != "outer$color")
21984        );
21985        for enumerator in ["color.RED", "color.GREEN"] {
21986            assert!(
21987                declarations.iter().any(|unit| unit.fq_name() == enumerator),
21988                "expected {enumerator}, got {declarations:?}"
21989            );
21990        }
21991        let children = parsed
21992            .children
21993            .get(color)
21994            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
21995        assert!(
21996            ["color.RED", "color.GREEN"]
21997                .iter()
21998                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
21999            "enumerators must hang off their enum: {children:?}"
22000        );
22001    }
22002
22003    #[test]
22004    fn c_file_mints_member_list_union_at_file_scope() {
22005        let source = "struct outer { union inner { int a; float b; } item; };\n";
22006        let parsed = parse_cpp_declarations(source, "u.c");
22007        let declarations = parsed.declarations();
22008        assert!(
22009            declarations
22010                .iter()
22011                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
22012            "expected a file-scope inner union, got {declarations:?}"
22013        );
22014        assert!(
22015            declarations
22016                .iter()
22017                .all(|unit| unit.fq_name() != "outer$inner")
22018        );
22019        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
22020        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
22021    }
22022
22023    /// A tag declared in a namespace member list is not a file-scope tag: the
22024    /// nearest enclosing non-aggregate scope is the namespace.
22025    #[test]
22026    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
22027        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
22028        let parsed = parse_cpp_declarations(source, "n.c");
22029        let declarations = parsed.declarations();
22030        let inner = declarations
22031            .iter()
22032            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
22033            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
22034        assert_eq!(inner.package_name(), "ns");
22035        assert!(
22036            declarations
22037                .iter()
22038                .all(|unit| unit.fq_name() != "ns.outer$inner")
22039        );
22040    }
22041
22042    /// Pins today's treatment of a tag declared inside a function body: the
22043    /// declaration walk does not descend into statement bodies, so no unit is
22044    /// minted for it in either dialect. C block scope is out of scope for the
22045    /// dialect change, and this test proves the change did not disturb it.
22046    #[test]
22047    fn function_local_tags_are_unchanged_in_both_dialects() {
22048        let source =
22049            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
22050        for name in ["y.c", "y.cpp"] {
22051            let parsed = parse_cpp_declarations(source, name);
22052            let declarations = parsed.declarations();
22053            assert!(
22054                declarations
22055                    .iter()
22056                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
22057                "{name}: {declarations:?}"
22058            );
22059            for tag in ["localtag", "deeper", "localtag$deeper"] {
22060                assert!(
22061                    declarations.iter().all(|unit| unit.fq_name() != tag),
22062                    "{name} must not mint {tag}, got {declarations:?}"
22063                );
22064            }
22065        }
22066    }
22067
22068    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
22069    /// re-scope: the typedef name is identical in both dialects.
22070    #[test]
22071    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
22072        let source = "typedef struct { int v; } T;\n";
22073        for name in ["t.c", "t.cpp"] {
22074            let parsed = parse_cpp_declarations(source, name);
22075            let declarations = parsed.declarations();
22076            assert!(
22077                declarations
22078                    .iter()
22079                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
22080                "{name}: {declarations:?}"
22081            );
22082        }
22083    }
22084
22085    /// The same principle for every anonymous aggregate the walk indexes, not
22086    /// just the typedef one: a promoted anonymous union in a member list, an
22087    /// anonymous struct given a receiver type by its declarator, a
22088    /// function-local aggregate, and one inside an inline member function
22089    /// body. The C++ reading used to mint none of these, so a pure C++
22090    /// workspace indexed no members at all (#3301).
22091    #[test]
22092    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
22093        let sources = [
22094            "struct outer { union { int a; float b; }; };\n",
22095            "struct outer { struct { int *ops; } sock; };\n",
22096            "void run(void) { struct { int v; } item; }\n",
22097            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
22098        ];
22099        for source in sources {
22100            let names = |name: &str| {
22101                parse_cpp_declarations(source, name)
22102                    .declarations()
22103                    .iter()
22104                    .map(|unit| unit.fq_name())
22105                    .collect::<std::collections::BTreeSet<_>>()
22106            };
22107            let c_names = names("a.c");
22108            let cpp_names = names("a.cpp");
22109            assert_eq!(
22110                c_names, cpp_names,
22111                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
22112            );
22113            assert!(
22114                c_names
22115                    .iter()
22116                    .any(|name| name.contains('.') && name != "outer"),
22117                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
22118            );
22119        }
22120    }
22121
22122    #[test]
22123    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
22124        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
22125        let parsed = parse_cpp_declarations(source, "socket.c");
22126        let declarations = parsed.declarations();
22127        assert_eq!(
22128            declarations
22129                .iter()
22130                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
22131                .count(),
22132            1,
22133            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
22134        );
22135        for expected in [
22136            "PAL_HANDLE",
22137            "PAL_HANDLE.sock",
22138            "PAL_HANDLE$sock",
22139            "PAL_HANDLE$sock.ops",
22140        ] {
22141            assert!(
22142                declarations.iter().any(|unit| unit.fq_name() == expected),
22143                "expected {expected}, got {declarations:?}"
22144            );
22145        }
22146    }
22147
22148    /// `class` is not C. Source that spells one in a `.c` file is not C code,
22149    /// so it keeps the C++ reading rather than acquiring a half-C identity.
22150    #[test]
22151    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
22152        let source = "class outer { class inner { int v; }; };\n";
22153        let c_parsed = parse_cpp_declarations(source, "k.c");
22154        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
22155        let c_declarations = c_parsed.declarations();
22156        let cpp_declarations = cpp_parsed.declarations();
22157        assert!(
22158            c_declarations
22159                .iter()
22160                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
22161            "{c_declarations:?}"
22162        );
22163        assert_eq!(
22164            c_declarations
22165                .iter()
22166                .map(|unit| unit.fq_name())
22167                .collect::<std::collections::BTreeSet<_>>(),
22168            cpp_declarations
22169                .iter()
22170                .map(|unit| unit.fq_name())
22171                .collect::<std::collections::BTreeSet<_>>()
22172        );
22173    }
22174
22175    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
22176    /// every (node, class-like name) pair a tree offers, and require the same
22177    /// answer from both.
22178    ///
22179    /// The release build has no `debug_assertions` agreement check, so this is
22180    /// what pins the two together there.  Both query orders are exercised:
22181    /// document order is what the walk does, and reverse order proves that a
22182    /// question about an earlier byte than one already answered is still
22183    /// filtered back to its own prefix rather than answered from the wider
22184    /// fold.
22185    ///
22186    /// Returns how many questions were answered with a namespace, so a fixture
22187    /// can assert it actually reached the path (#2754).
22188    fn namespace_forward_scan_agreement(source: &str) -> usize {
22189        let mut parser = tree_sitter::Parser::new();
22190        parser
22191            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22192            .unwrap();
22193        let tree = parser.parse(source, None).unwrap();
22194        let root = tree.root_node();
22195        let ancestry = ParentIndex::new(root);
22196
22197        let mut nodes = Vec::new();
22198        let mut names = std::collections::BTreeSet::new();
22199        let mut cursor = root.walk();
22200        let mut stack = vec![root];
22201        while let Some(node) = stack.pop() {
22202            if matches!(
22203                node.kind(),
22204                "class_specifier" | "struct_specifier" | "union_specifier"
22205            ) && let Some(name) = class_like_name(node, source, &ancestry)
22206            {
22207                names.insert(name);
22208            }
22209            nodes.push(node);
22210            stack.extend(node.named_children(&mut cursor));
22211        }
22212        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
22213        assert!(!names.is_empty(), "fixture declares no class-like name");
22214
22215        let mut answered = 0usize;
22216        for reversed in [false, true] {
22217            let mut scan = CppNamespaceForwardScan::default();
22218            let ordered: Vec<_> = if reversed {
22219                nodes.iter().rev().copied().collect()
22220            } else {
22221                nodes.clone()
22222            };
22223            answered = 0;
22224            for node in ordered {
22225                for name in &names {
22226                    scan.advance_to(root, node.start_byte(), source, &ancestry);
22227                    let carried = scan.unique_earlier_forward(name, node);
22228                    assert_eq!(
22229                        carried,
22230                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
22231                        "carried-forward scan and prefix scan disagree about {name} at \
22232                         {} node starting at byte {} (reversed order: {reversed})",
22233                        node.kind(),
22234                        node.start_byte()
22235                    );
22236                    answered += usize::from(carried.is_some());
22237                }
22238            }
22239        }
22240        answered
22241    }
22242
22243    /// A malformed namespace whose forward declarations are the only identity
22244    /// signal left for the class definitions tree-sitter pushed out to file
22245    /// scope.  Both recovered classes open the guard; only the first one is
22246    /// separated from the namespace by nothing but recovery trivia, so only the
22247    /// first one borrows.  The carried-forward scan has to reproduce both
22248    /// answers.
22249    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
22250namespace ns {
22251class Widget;
22252class Gadget;
22253int x = ;
22254}
22255class API Widget {
22256public:
22257    void first();
22258};
22259class API Gadget {
22260public:
22261    void second();
22262};
22263"#;
22264
22265    #[test]
22266    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
22267        assert!(
22268            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
22269            "the fixture must actually reach the namespace-borrow path"
22270        );
22271
22272        // Nothing here may be answered, and the two paths have to agree about
22273        // that too: a clean namespace is not an identity proof, two forwards of
22274        // one name are ambiguous rather than a guess, and a forward inside a
22275        // function body is not at namespace scope.
22276        for source in [
22277            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
22278            r#"#define API
22279namespace ns {
22280class Widget;
22281class Widget;
22282int x = ;
22283}
22284class API Widget {
22285public:
22286    void method();
22287};
22288"#,
22289            r#"#define API
22290namespace ns {
22291void host() {
22292    class Widget;
22293}
22294int x = ;
22295}
22296class API Widget {
22297public:
22298    void method();
22299};
22300"#,
22301        ] {
22302            assert_eq!(
22303                namespace_forward_scan_agreement(source),
22304                0,
22305                "no borrow is justified here: {source}"
22306            );
22307        }
22308    }
22309
22310    /// The fold is incremental, so a walk that asks about steadily later bytes
22311    /// must never re-fold a node an earlier question already folded, and must
22312    /// never skip one that lies between two questions.
22313    #[test]
22314    fn carried_forward_namespace_scan_folds_each_node_once() {
22315        let source = MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES;
22316        let mut parser = tree_sitter::Parser::new();
22317        parser
22318            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22319            .unwrap();
22320        let tree = parser.parse(source, None).unwrap();
22321        let root = tree.root_node();
22322        let ancestry = ParentIndex::new(root);
22323
22324        let mut incremental = CppNamespaceForwardScan::default();
22325        for cutoff in 0..=source.len() {
22326            incremental.advance_to(root, cutoff, source, &ancestry);
22327        }
22328        let mut whole = CppNamespaceForwardScan::default();
22329        whole.advance_to(root, source.len(), source, &ancestry);
22330
22331        let mut incremental_shape: Vec<_> = incremental
22332            .forwards
22333            .iter()
22334            .map(|(name, forwards)| {
22335                (
22336                    name.clone(),
22337                    forwards
22338                        .iter()
22339                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
22340                        .collect::<Vec<_>>(),
22341                )
22342            })
22343            .collect();
22344        let mut whole_shape: Vec<_> = whole
22345            .forwards
22346            .iter()
22347            .map(|(name, forwards)| {
22348                (
22349                    name.clone(),
22350                    forwards
22351                        .iter()
22352                        .map(|forward| (forward.start_byte, forward.package_name.clone()))
22353                        .collect::<Vec<_>>(),
22354                )
22355            })
22356            .collect();
22357        incremental_shape.sort();
22358        whole_shape.sort();
22359        for (_, forwards) in &mut incremental_shape {
22360            forwards.sort();
22361        }
22362        for (_, forwards) in &mut whole_shape {
22363            forwards.sort();
22364        }
22365
22366        assert!(!whole_shape.is_empty(), "fixture folds no forward");
22367        assert_eq!(
22368            incremental_shape, whole_shape,
22369            "one byte at a time must fold exactly what one whole pass folds"
22370        );
22371    }
22372
22373    /// Drive the region reparse and the whitespace-padded reparse it replaced
22374    /// over the same region and require identical trees.
22375    ///
22376    /// The release build has no `debug_assertions` agreement check, so this is
22377    /// what pins the two together there. Each body is reparsed at its own
22378    /// offset and again after a long prefix, because the prefix is the whole
22379    /// difference between the two techniques: the padded parse lexes it as
22380    /// whitespace, the included-range parse never sees it, and the tree has to
22381    /// come out the same either way (#2788).
22382    fn fragmented_class_reparse_agreement(body: &str) {
22383        for prefix in [
22384            String::new(),
22385            "// leading comment\n".to_string(),
22386            // A body starts just after its class head's `{`, which is normally
22387            // mid-line: the padded parse then has spaces before the region on
22388            // the region's own line, and the included-range parse has nothing
22389            // at all before it.
22390            "class Widget : public Base { ".to_string(),
22391            "namespace filler {\n".to_string()
22392                + &"struct Filler { int member; };\n".repeat(200)
22393                + "}\n",
22394            "namespace filler {\n".to_string()
22395                + &"struct Filler { int member; };\n".repeat(200)
22396                + "}\nclass Widget : public Base { ",
22397        ] {
22398            let source = format!("{prefix}{body}");
22399            let start = prefix.len();
22400            let end = source.len();
22401            let region = cpp_reparse_fragmented_class_body(&source, start, end)
22402                .expect("the region reparse must produce a tree");
22403            let padded = cpp_reparse_padded_class_body(&source, start, end)
22404                .expect("the padded reparse must produce a tree");
22405            assert_eq!(
22406                cpp_tree_shape(&region),
22407                cpp_tree_shape(&padded),
22408                "region and padded reparse disagree at offset {start} of {end} bytes"
22409            );
22410            assert_eq!(
22411                region.root_node().start_byte(),
22412                start,
22413                "the reparsed region keeps its original offsets"
22414            );
22415        }
22416    }
22417
22418    #[test]
22419    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
22420        // A conditional immediately after an access label: the shape the padded
22421        // technique was kept for, because the directive and its macro name
22422        // become an ERROR plus the following declaration's apparent type.
22423        fragmented_class_reparse_agreement(
22424            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
22425        );
22426        fragmented_class_reparse_agreement(
22427            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
22428        );
22429        // The merged inline constructor and the nested fragmented bodies the
22430        // #938 recovery reads out of a reparse.
22431        fragmented_class_reparse_agreement(
22432            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
22433        );
22434        fragmented_class_reparse_agreement(
22435            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
22436        );
22437        // A body that is not member-shaped at all still has to reparse the same
22438        // way, because the admission gate reads the tree to reject it.
22439        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
22440    }
22441
22442    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
22443        let mut siblings = Vec::new();
22444        let mut anchor = node;
22445        while let Some(parent) = anchor.parent() {
22446            let at_translation_unit = parent.kind() == "translation_unit";
22447            let mut sibling = anchor.next_named_sibling();
22448            while let Some(current) = sibling {
22449                if at_translation_unit
22450                    && (current.kind() == "namespace_definition"
22451                        || (current.kind() == "function_definition"
22452                            && first_class_like_child(current).is_some()))
22453                {
22454                    return siblings;
22455                }
22456                siblings.push(current);
22457                if cpp_is_stray_close_brace(current, source) {
22458                    if let Some(semicolon) = current
22459                        .next_named_sibling()
22460                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
22461                    {
22462                        siblings.push(semicolon);
22463                    }
22464                    return siblings;
22465                }
22466                if current.start_byte() >= node.end_byte()
22467                    && matches!(current.kind(), "ERROR" | "labeled_statement")
22468                    && cpp_nested_stray_close_brace(current, source).is_some()
22469                {
22470                    return siblings;
22471                }
22472                sibling = current.next_named_sibling();
22473            }
22474            anchor = parent;
22475        }
22476        siblings
22477    }
22478
22479    #[test]
22480    fn following_named_siblings_cursor_matches_native_traversal() {
22481        // The damaged #2924 shape scatters members across container siblings,
22482        // and the anonymous punctuation forces the cursor to filter children
22483        // exactly as Node::next_named_sibling does.
22484        let source = r#"
22485namespace api {
22486class PROJECT_API(2, 0) Widget : public virtual Base {
22487  public:
22488    Widget() {}
22489    void first();
22490};
22491enum After { Value };
22492}
22493"#;
22494        let mut parser = tree_sitter::Parser::new();
22495        parser
22496            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22497            .unwrap();
22498        let tree = parser.parse(source, None).unwrap();
22499        let root = tree.root_node();
22500        assert!(root.has_error(), "fixture must remain damaged");
22501
22502        let ancestry = ParentIndex::new(root);
22503        let mut stack = vec![root];
22504        let mut checked = 0;
22505        let mut damaged = 0;
22506        let mut anonymous = 0;
22507        let mut nonempty_sequences = 0;
22508        let mut named_then_anonymous = 0;
22509        while let Some(node) = stack.pop() {
22510            let expected = native_following_named_siblings(node, source)
22511                .iter()
22512                .map(Node::id)
22513                .collect::<Vec<_>>();
22514            let actual = cpp_following_named_siblings(node, source, &ancestry)
22515                .iter()
22516                .map(Node::id)
22517                .collect::<Vec<_>>();
22518            assert_eq!(
22519                actual,
22520                expected,
22521                "cursor traversal diverged at byte {}",
22522                node.start_byte()
22523            );
22524
22525            let mut cursor = node.walk();
22526            let children = node.children(&mut cursor).collect::<Vec<_>>();
22527            stack.extend(children.iter().copied());
22528            checked += 1;
22529            damaged += usize::from(node.is_error() || node.is_missing());
22530            anonymous += usize::from(!node.is_named());
22531            nonempty_sequences += usize::from(!actual.is_empty());
22532            named_then_anonymous += usize::from(
22533                children
22534                    .windows(2)
22535                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
22536            );
22537        }
22538        assert!(checked > 0, "fixture must provide nodes to compare");
22539        assert!(damaged > 0, "fixture must cover damaged nodes");
22540        assert!(anonymous > 0, "fixture must cover anonymous nodes");
22541        assert!(
22542            nonempty_sequences > 0,
22543            "fixture must exercise following named siblings"
22544        );
22545        assert!(
22546            named_then_anonymous > 0,
22547            "fixture must exercise anonymous nodes following a named anchor"
22548        );
22549    }
22550
22551    /// A header shaped like the generated ones this walk is slow on: many
22552    /// enums, classes whose members share the enums' names, nested enums, an
22553    /// ownerless enumerator, and namespaced repeats of all of it.
22554    fn many_enums_and_mixed_declarations() -> String {
22555        let mut source = String::from("#define API\nenum Empty {};\nenum API Loose { KEPT, };\n");
22556        for index in 0..40 {
22557            let _ = write!(
22558                source,
22559                "enum Color{index} {{ RED{index}, GREEN{index} }};\n\
22560                 struct Holder{index} {{ int Color{index}; enum Inner{index} {{ A{index} }}; }};\n\
22561                 class Color{index}Like {{ public: int member{index}; }};\n"
22562            );
22563        }
22564        source.push_str("namespace outer {\n");
22565        for index in 0..20 {
22566            let _ = write!(
22567                source,
22568                "enum Shade{index} {{ DARK{index} }};\n\
22569                 struct Shade{index}Holder {{ int field{index}; }};\n"
22570            );
22571        }
22572        source.push_str("}\n");
22573        source
22574    }
22575
22576    /// Drive [`CppFieldOwnerIndex`] and the declaration scan it replaced over
22577    /// every question a fixture's declarations can ask, and require the same
22578    /// answer from both.
22579    ///
22580    /// The release build has no `debug_assertions` agreement check, so this is
22581    /// what pins the two together there. The index is fed one declaration at a
22582    /// time and every question is re-asked after each one, which is what proves
22583    /// the incremental record agrees -- a whole-set rebuild would pass a weaker
22584    /// test. Each of the fixture's own fields is also fed in restated as a
22585    /// declaration of another file: the scan ignores those because it asks
22586    /// about the asking unit's own source, and the index has to ignore them for
22587    /// the same reason.
22588    ///
22589    /// Returns how many questions the fixture answered `true`, so a caller can
22590    /// assert that it actually reached the path (#2786).
22591    fn field_owner_index_agreement(source: &str, name: &str) -> usize {
22592        let parsed = parse_cpp_declarations(source, name);
22593        let file = ProjectFile::new(std::env::temp_dir(), name);
22594        let elsewhere = ProjectFile::new(std::env::temp_dir(), "elsewhere.hpp");
22595
22596        let mut declarations: Vec<CodeUnit> = parsed.declarations().iter().cloned().collect();
22597        declarations.sort_by_key(|unit| (unit.fq_name(), unit.kind()));
22598
22599        let foreign: Vec<CodeUnit> = declarations
22600            .iter()
22601            .filter(|unit| unit.kind() == CodeUnitType::Field)
22602            .map(|unit| {
22603                CodeUnit::new_fq(
22604                    elsewhere.clone(),
22605                    unit.kind(),
22606                    unit.package_name().to_string(),
22607                    unit.short_name().to_string(),
22608                    unit.fq().clone(),
22609                )
22610            })
22611            .collect();
22612
22613        // One owner chain deeper than any C++ short name reaches today
22614        // (`cpp_member_fq`: at most one `.`, separating the owner chain from
22615        // the member). The scan asks `starts_with("owner.")`, so a field like
22616        // this answers for every owner in its chain, and the index has to
22617        // record every one of them rather than only the innermost.
22618        let mut packages: Vec<String> = declarations
22619            .iter()
22620            .map(|unit| unit.package_name().to_string())
22621            .collect();
22622        packages.push(String::new());
22623        packages.sort();
22624        packages.dedup();
22625        let deeper: Vec<CodeUnit> = packages
22626            .iter()
22627            .map(|package_name| {
22628                CodeUnit::new_fq(
22629                    file.clone(),
22630                    CodeUnitType::Field,
22631                    package_name.clone(),
22632                    "SynthOwner.middle.leaf".to_string(),
22633                    cpp_member_fq(package_name, "SynthOwner.middle.leaf"),
22634                )
22635            })
22636            .collect();
22637
22638        // Every (package, owner) pair anything could ask about: each unit's own
22639        // short name, each dotted prefix of it, and the empty owner an
22640        // anonymous enum asks with (#2140).
22641        let mut questions: Vec<(String, String)> = Vec::new();
22642        for unit in declarations.iter().chain(deeper.iter()) {
22643            let package_name = unit.package_name().to_string();
22644            let short_name = unit.short_name();
22645            questions.push((package_name.clone(), short_name.to_string()));
22646            questions.push((package_name.clone(), String::new()));
22647            for (offset, _) in short_name.match_indices('.') {
22648                questions.push((package_name.clone(), short_name[..offset].to_string()));
22649            }
22650        }
22651        questions.sort();
22652        questions.dedup();
22653
22654        let mut index = CppFieldOwnerIndex::default();
22655        let mut recorded: Vec<&CodeUnit> = Vec::new();
22656        let mut answered = 0usize;
22657        for unit in foreign
22658            .iter()
22659            .chain(declarations.iter())
22660            .chain(deeper.iter())
22661        {
22662            index.record(unit, &file);
22663            recorded.push(unit);
22664            for (package_name, owner_short_name) in &questions {
22665                let carried = index.owns_fields(package_name, owner_short_name);
22666                assert_eq!(
22667                    carried,
22668                    cpp_declarations_hold_owned_fields(
22669                        recorded.iter().copied(),
22670                        &file,
22671                        package_name,
22672                        owner_short_name
22673                    ),
22674                    "the carried field index and the declaration scan disagree about \
22675                     {package_name:?}/{owner_short_name:?} after recording {}",
22676                    unit.fq_name()
22677                );
22678                answered += usize::from(carried);
22679            }
22680        }
22681
22682        // The whole-set build the first question performs must land on the same
22683        // index the incremental record built.
22684        let rebuilt = CppFieldOwnerIndex::of(
22685            foreign
22686                .iter()
22687                .chain(declarations.iter())
22688                .chain(deeper.iter()),
22689            &file,
22690        );
22691        for (package_name, owner_short_name) in &questions {
22692            assert_eq!(
22693                rebuilt.owns_fields(package_name, owner_short_name),
22694                index.owns_fields(package_name, owner_short_name),
22695                "a rebuilt index must answer what the incremental one answers for \
22696                 {package_name:?}/{owner_short_name:?}"
22697            );
22698        }
22699        answered
22700    }
22701
22702    /// The one thing the field index cannot absorb by addition: a deferred
22703    /// replacement of a declaration that owns children removes those children.
22704    ///
22705    /// The first enum builds the index, the struct records `Color.RED` into it,
22706    /// the body-less second `Color` replaces the first and takes `Color.RED`
22707    /// with it, and the last enum then asks about owner `Color`. An index that
22708    /// survived that removal answers `true` where the declarations say `false`,
22709    /// which is exactly what the in-walk agreement assertion catches (#2786).
22710    #[test]
22711    fn a_replacement_that_removes_children_drops_the_field_index() {
22712        let source =
22713            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
22714        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
22715        let mut names: Vec<_> = parsed
22716            .declarations()
22717            .iter()
22718            .map(|unit| unit.fq_name())
22719            .collect();
22720        names.sort();
22721        assert_eq!(
22722            names,
22723            vec![
22724                "Color".to_string(),
22725                "First".to_string(),
22726                "First.A".to_string()
22727            ],
22728            "the replaced Color owns no field any more"
22729        );
22730    }
22731
22732    /// A recovery that re-declares what the file already declared mints
22733    /// nothing.
22734    ///
22735    /// The reparse walk replaces the outer `Widget`, which removes its method,
22736    /// and then re-creates that method from the region. Creation alone would
22737    /// call the method recovered; it was there before the recovery opened, so
22738    /// the recovered set is empty and only the region's own reparse window is
22739    /// recorded (#2787).
22740    #[test]
22741    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
22742        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
22743                      BEGIN_NS\n\
22744                      namespace demo { struct Widget { void doWork(); }; }\n\
22745                      END_NS\n";
22746        let parsed = parse_cpp_declarations(source, "restored.cpp");
22747        let recovered: Vec<String> = parsed
22748            .materialization_records
22749            .iter()
22750            .filter_map(|record| match record {
22751                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
22752                _ => None,
22753            })
22754            .collect();
22755        assert!(
22756            recovered.is_empty(),
22757            "the region declares nothing the file did not already declare: {recovered:?}"
22758        );
22759        let mut names: Vec<String> = parsed
22760            .declarations()
22761            .iter()
22762            .map(|unit| unit.fq_name())
22763            .collect();
22764        names.sort();
22765        assert_eq!(
22766            names,
22767            vec![
22768                "demo".to_string(),
22769                "demo.Widget".to_string(),
22770                "demo.Widget.doWork".to_string(),
22771            ]
22772        );
22773    }
22774
22775    /// Four macro-sentinel recoveries in one file (#941). Each one must record
22776    /// exactly the declarations it minted -- not the ones an earlier recovery
22777    /// minted, and not the file's other declarations -- in start-byte order,
22778    /// and each record must carry its own reparse window (#2787).
22779    #[test]
22780    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
22781        let mut source = String::new();
22782        for index in 0..4 {
22783            let _ = write!(
22784                source,
22785                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
22786            );
22787        }
22788        source.push_str("void outside() {}\n");
22789        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
22790
22791        let recovered: Vec<(String, (usize, usize))> = parsed
22792            .materialization_records
22793            .iter()
22794            .filter_map(|record| match record {
22795                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
22796                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
22797                }
22798                _ => None,
22799            })
22800            .collect();
22801
22802        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
22803        for index in 0..4 {
22804            // The window the reparse covers: everything after the opening
22805            // sentinel token up to the newline before the closing one.
22806            let region = format!("namespace demo{index}");
22807            let region_start = source.find(&region).expect("each region is in the source");
22808            let start = source[..region_start]
22809                .rfind("BEGIN_NS")
22810                .expect("each region opens with a sentinel")
22811                + "BEGIN_NS".len();
22812            let end = start
22813                + source[start..]
22814                    .find("END_NS")
22815                    .expect("each region closes with a sentinel")
22816                - 1;
22817            let window = (start, end);
22818            for name in [
22819                format!("demo{index}"),
22820                format!("demo{index}.Widget{index}"),
22821                format!("demo{index}.Widget{index}.doWork{index}"),
22822            ] {
22823                expected.push((name, window));
22824            }
22825        }
22826        assert_eq!(
22827            recovered, expected,
22828            "each recovery records its own minted declarations, in order"
22829        );
22830        assert!(
22831            parsed
22832                .declarations()
22833                .iter()
22834                .any(|unit| unit.fq_name() == "outside"),
22835            "the declaration outside every region stays parsed and unrecovered"
22836        );
22837    }
22838
22839    #[test]
22840    fn carried_forward_field_index_answers_what_the_declaration_scan_answers() {
22841        assert!(
22842            field_owner_index_agreement(&many_enums_and_mixed_declarations(), "many-enums.hpp") > 0,
22843            "the fixture must actually own fields"
22844        );
22845
22846        // The shapes that make the two implementations diverge if the index
22847        // records the wrong keys: a nested enum's owner is its whole dotted
22848        // chain, a class named like an enum owns fields under that same name, a
22849        // `$` in a short name is an owner separator the dotted prefix rule must
22850        // not split on, and the same enum name in two namespaces is two owners.
22851        for (source, name) in [
22852            ("struct S { enum E { V }; };\n", "nested.hpp"),
22853            (
22854                "enum Color { RED };\nstruct Color { int RED; };\n",
22855                "class-like.c",
22856            ),
22857            ("struct Outer { struct Inner { int V; }; };\n", "sigil.hpp"),
22858            (
22859                "enum E { V };\nnamespace ns { enum E { V }; }\n",
22860                "repeated.hpp",
22861            ),
22862            ("#define API\nenum API Loose { KEPT, };\n", "ownerless.hpp"),
22863        ] {
22864            field_owner_index_agreement(source, name);
22865        }
22866    }
22867
22868    /// `blocks` copies of one plain class-with-methods template. The class the
22869    /// question is about is always the first one, so the only thing that
22870    /// changes between two of these sources is how much unrelated tree
22871    /// surrounds it.
22872    fn repeated_class_blocks(blocks: usize) -> String {
22873        let mut source = String::from("namespace demo {\n");
22874        for index in 0..blocks {
22875            let _ = write!(
22876                source,
22877                "\nclass Widget{index} {{\npublic:\n    int translate() const {{ return {index}; }}\n    int helper(const Widget{index}& other) const {{ return other.translate(); }}\nprivate:\n    int field = {index};\n}};\n"
22878            );
22879        }
22880        source.push_str("\n}\n");
22881        source
22882    }
22883
22884    /// #1496: asking whether a recovered class shape claims one range must cost
22885    /// the path to that range, not a pass over the whole translation unit.
22886    ///
22887    /// The C++ inverse scan asks this once per candidate type reference, so a
22888    /// whole-tree walk makes one file's scan quadratic in its own size. Both
22889    /// sources here answer `None` -- these are plain classes that no recovery
22890    /// shape claims -- which is exactly the case that used to pay full price.
22891    #[test]
22892    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
22893        let mut answers = Vec::new();
22894        let mut visits = Vec::new();
22895        let mut node_counts = Vec::new();
22896        for blocks in [200usize, 400] {
22897            let source = repeated_class_blocks(blocks);
22898            let mut parser = tree_sitter::Parser::new();
22899            parser
22900                .set_language(&tree_sitter_cpp::LANGUAGE.into())
22901                .unwrap();
22902            let tree = parser.parse(&source, None).unwrap();
22903            let start_byte = source.find("class Widget0 ").expect("first class");
22904            let end_byte = start_byte
22905                + source[start_byte..]
22906                    .find("};")
22907                    .expect("first class terminator")
22908                + "};".len();
22909            let range = Range {
22910                start_byte,
22911                end_byte,
22912                start_line: 0,
22913                end_line: 0,
22914            };
22915            reset_recovered_class_body_node_visits_for_test();
22916            let recovered_export_classes =
22917                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
22918            answers.push(recovered_class_body_at(
22919                &recovered_export_classes,
22920                tree.root_node(),
22921                &source,
22922                "Widget0",
22923                &range,
22924            ));
22925            visits.push(recovered_class_body_node_visits_for_test());
22926            let mut nodes = 0usize;
22927            let mut stack = vec![tree.root_node()];
22928            while let Some(node) = stack.pop() {
22929                nodes += 1;
22930                let mut cursor = node.walk();
22931                stack.extend(node.named_children(&mut cursor));
22932            }
22933            node_counts.push(nodes);
22934        }
22935
22936        assert_eq!(
22937            answers,
22938            vec![None, None],
22939            "no recovered shape claims a plain class"
22940        );
22941        assert_eq!(
22942            visits[0], visits[1],
22943            "the walk must follow the range's own path, so doubling the unrelated \
22944             classes must not change the node count: {visits:?} over trees of \
22945             {node_counts:?} nodes"
22946        );
22947        assert!(
22948            visits[1] * 20 < node_counts[1],
22949            "the walk must stay far below one pass over the tree: {visits:?} over \
22950             trees of {node_counts:?} nodes"
22951        );
22952    }
22953
22954    #[test]
22955    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
22956        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
22957        let mut parser = tree_sitter::Parser::new();
22958        parser
22959            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22960            .expect("C++ grammar");
22961        let tree = parser.parse(source, None).expect("fixture tree");
22962        let mut stack = vec![tree.root_node()];
22963        let mut recovered = None;
22964        while let Some(node) = stack.pop() {
22965            if node.kind() == "field_declaration"
22966                && let Some(field) = recovered_function_like_field_declarator(node, source)
22967            {
22968                recovered = Some((node, field.name));
22969                break;
22970            }
22971            let mut cursor = node.walk();
22972            stack.extend(node.named_children(&mut cursor));
22973        }
22974        let (declaration, name) = recovered.unwrap_or_else(|| {
22975            panic!(
22976                "pointer-wrapped macro field was not recovered: {}",
22977                tree.root_node().to_sexp()
22978            )
22979        });
22980        assert_eq!(node_text(name, source), "handshake");
22981        let recovered =
22982            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
22983        assert_eq!(recovered.pointer_depth(), 1);
22984        assert_eq!(
22985            render_cpp_field_signature(declaration, name, source),
22986            "struct handshake * handshake;"
22987        );
22988    }
22989
22990    #[test]
22991    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
22992        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
22993        let mut parser = tree_sitter::Parser::new();
22994        parser
22995            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22996            .expect("C++ grammar");
22997        let tree = parser.parse(source, None).expect("fixture tree");
22998        let mut stack = vec![tree.root_node()];
22999        let mut recovered = None;
23000        while let Some(node) = stack.pop() {
23001            if let Some(field) = recovered_pyobject_head_field(node, source) {
23002                recovered = Some((node, field));
23003                break;
23004            }
23005            let mut cursor = node.walk();
23006            stack.extend(node.named_children(&mut cursor));
23007        }
23008        let (declaration, recovered) = recovered.unwrap_or_else(|| {
23009            panic!(
23010                "pointer field after PyObject_HEAD was not recovered: {}",
23011                tree.root_node().to_sexp()
23012            )
23013        });
23014        assert_eq!(node_text(recovered.name, source), "image");
23015        assert_eq!(recovered.pointer_depth(), 1);
23016        assert_eq!(
23017            render_cpp_field_signature(declaration, recovered.declarator, source),
23018            "ImagingObject *image;"
23019        );
23020    }
23021
23022    /// The C source shapes whose callable modifiers the walk reads, and what
23023    /// each one states. C has no member functions at all, so the only fact its
23024    /// declarations carry is Unknown; linkage is separate. The `static`
23025    /// a C function writes is internal linkage, never a receiver.
23026    #[test]
23027    fn callable_metadata_records_c_receiver_contracts_structurally() {
23028        const SOURCE: &str = concat!(
23029            "static long accumulate(const long *lines, int count) { return count; }\n",
23030            "\n",
23031            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
23032        );
23033        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
23034
23035        assert_eq!(
23036            recorded_callable_modifiers(&parsed),
23037            vec![
23038                "accumulate decl_only=false static=false constructor=false \
23039                 visibility=Unknown"
23040                    .to_string(),
23041                "total decl_only=false static=false constructor=false visibility=Unknown"
23042                    .to_string(),
23043            ],
23044        );
23045    }
23046
23047    /// The C++ source shapes whose callable modifiers the walk reads.
23048    ///
23049    /// C++ states the receiver fact in the declaration's position: a class-body
23050    /// member binds the object it is selected on unless it writes `static`, and
23051    /// its access is the one the body's specifier ladder is under. The
23052    /// out-of-line definitions state neither -- C++ forbids them from repeating
23053    /// `static` and they write no access specifier -- which is why
23054    /// `Client::count` is recorded static only on the declaration-only row, the
23055    /// one whose `static` is actually written. An in-class prototype and the
23056    /// out-of-line definition completing it in this translation unit are two
23057    /// rows of one code unit (#3508), so their rows share the unit's name and
23058    /// `decl_only` is what separates them.
23059    #[test]
23060    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
23061        const SOURCE: &str = concat!(
23062            "namespace shop {\n",
23063            "\n",
23064            "class Client {\n",
23065            "public:\n",
23066            "    Client();\n",
23067            "    void send(int order);\n",
23068            "    static int count();\n",
23069            "protected:\n",
23070            "    virtual int audit(int n) const { return n; }\n",
23071            "private:\n",
23072            "    int tally(int n) { return n; }\n",
23073            "};\n",
23074            "\n",
23075            "struct Ledger {\n",
23076            "    void record(int n) { (void)n; }\n",
23077            "};\n",
23078            "\n",
23079            "Client::Client() {}\n",
23080            "void Client::send(int order) { (void)order; }\n",
23081            "int Client::count() { return 0; }\n",
23082            "\n",
23083            "int total(int order) { return order; }\n",
23084            "\n",
23085            "}\n",
23086        );
23087        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
23088
23089        assert_eq!(
23090            recorded_callable_modifiers(&parsed),
23091            vec![
23092                "shop.Client.Client decl_only=false static=false constructor=true \
23093                 visibility=Unknown"
23094                    .to_string(),
23095                "shop.Client.Client decl_only=true static=false constructor=true \
23096                 visibility=Public"
23097                    .to_string(),
23098                "shop.Client.audit decl_only=false static=false constructor=false \
23099                 visibility=Protected"
23100                    .to_string(),
23101                "shop.Client.count decl_only=false static=false constructor=false \
23102                 visibility=Unknown"
23103                    .to_string(),
23104                "shop.Client.count decl_only=true static=true constructor=false \
23105                 visibility=Public"
23106                    .to_string(),
23107                "shop.Client.send decl_only=false static=false constructor=false \
23108                 visibility=Unknown"
23109                    .to_string(),
23110                "shop.Client.send decl_only=true static=false constructor=false \
23111                 visibility=Public"
23112                    .to_string(),
23113                "shop.Client.tally decl_only=false static=false constructor=false \
23114                 visibility=Private"
23115                    .to_string(),
23116                "shop.Ledger.record decl_only=false static=false constructor=false \
23117                 visibility=Public"
23118                    .to_string(),
23119                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
23120                    .to_string(),
23121            ],
23122        );
23123    }
23124
23125    /// One readable row per recorded callable signature entry.
23126    ///
23127    /// An in-class prototype and the out-of-line definition completing it are
23128    /// two entries -- of one code unit when one translation unit holds both
23129    /// (#3508) -- and the two state different modifier facts, so the row says
23130    /// which declaration role it read.
23131    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
23132        let mut rows = parsed
23133            .signature_metadata
23134            .iter()
23135            .flat_map(|(unit, entries)| {
23136                entries.iter().map(move |metadata| {
23137                    assert!(
23138                        metadata.callable_modifiers_recorded(),
23139                        "{} must record that the walk read its declaration shape",
23140                        unit.fq_name()
23141                    );
23142                    format!(
23143                        "{} decl_only={} static={} constructor={} visibility={:?}",
23144                        unit.fq_name(),
23145                        metadata.is_declaration_only(),
23146                        metadata.callable_is_static(),
23147                        metadata.callable_is_constructor(),
23148                        metadata
23149                            .callable_declared_visibility()
23150                            .expect("a recorded modifier set states a visibility"),
23151                    )
23152                })
23153            })
23154            .collect::<Vec<_>>();
23155        rows.sort();
23156        rows
23157    }
23158}