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 crate::structural::{CPP_STRUCTURAL_SPEC, is_cpp_preprocessor_conditional};
10use crate::template_heads::cpp_out_of_line_owner_names;
11pub use brokk_bifrost_core::analyzer::common::node_source_text as node_text;
12use brokk_bifrost_core::analyzer::common::{
13    parse_source_ranges_with_cancellation, parse_source_region,
14};
15pub use brokk_bifrost_core::analyzer::cpp_facts::{
16    CppAccessSpecifier, CppBaseSpecifierFact, CppComparableNode, CppComparableParameter,
17    CppComparableSlot,
18};
19use brokk_bifrost_core::analyzer::fq_name::{
20    FqName, SegmentKind, joined_segments, normalize_joined, segment_interner,
21};
22use brokk_bifrost_core::analyzer::model::LanguageDialect;
23use brokk_bifrost_core::analyzer::model::{
24    CallableArity, CallableLinkage, CodeUnitType, CppFieldLinkage, CppTemplateAliasTargetMetadata,
25    CppTemplateExpression, CppTemplateMetadata, CppTemplateParameterKind,
26    CppTemplateParameterMetadata, CppTemplateTerm, DispatchExtensibility, ImportInfo,
27    ParameterMetadata, ParseCompleteness, Range, SignatureMetadata, StructuredTypeIdentity,
28    StructuredTypeIdentityBuilder, StructuredTypeName, StructuredTypeNodeId,
29};
30use brokk_bifrost_core::analyzer::parsed_file::{ParsedFile, ParsedSourceFacts, SourceImportFact};
31use brokk_bifrost_core::analyzer::source_facts::{
32    PrimarySourceFactCollector, SourceDeclarationId, SourceImportId, SourceOccurrenceId,
33    SourceOccurrenceProvenance,
34};
35use brokk_bifrost_core::analyzer::statement_facts::StatementFactRows;
36use brokk_bifrost_core::analyzer::structural::adapter_helpers::node_range;
37use brokk_bifrost_core::analyzer::structural::collector::StructuralFactCollector;
38use brokk_bifrost_core::analyzer::structural::materialization::{
39    GenerationKind, MaterializationRecord,
40};
41use brokk_bifrost_core::analyzer::structural::resolution::DeclaredVisibility;
42use brokk_bifrost_core::analyzer::structural::spec::{CompiledKinds, StructuralSpec};
43use brokk_bifrost_core::analyzer::symbol_path::parse_symbol_path;
44use brokk_bifrost_core::analyzer::tree_walk::{
45    NodeKindIds, ParentIndex, WalkControl, child_by_field_or_named_child_of_kinds, children_iter,
46    named_children_iter, push_children_reversed, push_named_children_reversed, subtree_contains,
47    walk_named_tree_preorder,
48};
49use brokk_bifrost_core::analyzer::usages::common::same_node;
50use brokk_bifrost_core::analyzer::{CodeUnit, Language, ProjectFile};
51use brokk_bifrost_core::hash::{HashMap, HashSet};
52use regex::Regex;
53use std::cell::RefCell;
54use std::sync::Arc;
55use tree_sitter::{Node, Tree};
56
57/// Strip a tag keyword from the type spelling returned by C++ tooling.
58pub(crate) fn strip_tag_type_prefix(value: &str) -> &str {
59    let value = value.trim_start_matches("const ");
60    value
61        .strip_prefix("struct ")
62        .or_else(|| value.strip_prefix("class "))
63        .or_else(|| value.strip_prefix("enum "))
64        .unwrap_or(value)
65        .trim()
66}
67
68/// Push per-component [`SegmentKind::Package`] segments for a C++ namespace
69/// path stored in its legacy `::`-joined form (`cutlass::gemm::warp`). The
70/// `::` head is exactly the mixed-separator store issue #1163 is about; the
71/// structured form records each namespace component, and the equivalence check
72/// renders it natively (with `::` between adjacent Package segments) so it
73/// round-trips to the legacy string. Splitting the already-joined string here is
74/// the M1 bridge — the legacy strings stay authoritative until M3.
75fn cpp_push_package(fq: &mut FqName, package_name: &str) {
76    for component in joined_segments(package_name, CPP_PACKAGE_SEPARATOR) {
77        fq.push(segment_interner().intern(component, SegmentKind::Package));
78    }
79}
80
81/// C++ namespace paths are stored `::`-joined in `package_name` (issue #1163).
82const CPP_PACKAGE_SEPARATOR: &str = "::";
83
84/// Push per-class segments for a nested-class chain stored in Bifrost's legacy
85/// `$`-joined `short_name` form (`Outer$Inner`, issue #1121). The outermost
86/// class is a plain [`SegmentKind::Type`]; every subsequently nested class is
87/// [`SegmentKind::Nested`], which renders its `$` join unconditionally (the
88/// same mechanism python/php/ruby's `$`-joined nesting already uses) — so no
89/// cpp-specific native rendering rule is needed for this chain.
90fn cpp_push_type_chain(fq: &mut FqName, chain: &str) {
91    let mut first = true;
92    // fqname-M4: sanctioned M1 construction bridge — this BUILDS the FqName's Type/Nested
93    // segments from the legacy `$`-joined nested-class chain at emission; it is the interning
94    // entry point, not re-inference of an already-structured name.
95    for component in chain.split('$').filter(|c| !c.is_empty()) {
96        let kind = if first {
97            SegmentKind::Type
98        } else {
99            SegmentKind::Nested
100        };
101        fq.push(segment_interner().intern(component, kind));
102        first = false;
103    }
104}
105
106/// Structured name for a C++ namespace module: every `::`-separated component is
107/// a [`SegmentKind::Package`] segment (the legacy unit stores the whole path in
108/// `short_name` with an empty `package_name`).
109fn cpp_namespace_fq(full_name: &str) -> FqName {
110    let mut fq = FqName::new();
111    cpp_push_package(&mut fq, full_name);
112    fq
113}
114
115/// The per-level namespace components a `namespace_definition`'s `name` field
116/// declares.
117///
118/// A C++17 nested definition (`namespace a::b::c`) parses as a
119/// `nested_namespace_specifier` whose named children are the per-level
120/// `namespace_identifier`s plus, for three or more levels, a further
121/// `nested_namespace_specifier`; the `::` separators, the optional per-level
122/// `inline`, and the leading global `::` are all anonymous tokens the walk
123/// skips. Reading those nodes keeps the shorthand on the same one-level-per-
124/// segment path as the expanded `namespace a { namespace b { } }` form.
125///
126/// A shape outside that grammar is the deliberately ill-formed source the
127/// diagnostic corpora carry. Those keep their historical single-component
128/// reading of the raw name text, which the caller still joins to the lexical
129/// namespace exactly as before.
130fn cpp_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
131    let mut components = Vec::new();
132    let mut stack = vec![node];
133    while let Some(current) = stack.pop() {
134        match current.kind() {
135            "namespace_identifier" | "identifier" => {
136                components.push(normalize_cpp_whitespace(node_text(current, source)));
137            }
138            "nested_namespace_specifier" => {
139                for index in (0..current.named_child_count()).rev() {
140                    stack.push(
141                        current
142                            .named_child(index)
143                            .expect("index below the node's own named child count"),
144                    );
145                }
146            }
147            _ => return cpp_raw_namespace_name_components(node, source),
148        }
149    }
150    if components.iter().any(String::is_empty) {
151        return cpp_raw_namespace_name_components(node, source);
152    }
153    components
154}
155
156/// [`cpp_namespace_name_components`] with each level's own name token range,
157/// for the one-Module-per-level declarations of the `namespace a::b`
158/// shorthand: the name a level records must come from the token that spells
159/// it (#4292), not from whichever construct the walk reached first.
160fn cpp_namespace_name_component_ranges(node: Node<'_>, source: &str) -> Vec<(String, Range)> {
161    let mut components = Vec::new();
162    let mut stack = vec![node];
163    while let Some(current) = stack.pop() {
164        match current.kind() {
165            "namespace_identifier" | "identifier" => {
166                components.push((
167                    normalize_cpp_whitespace(node_text(current, source)),
168                    cpp_declaration_range(current),
169                ));
170            }
171            "nested_namespace_specifier" => {
172                for index in (0..current.named_child_count()).rev() {
173                    stack.push(
174                        current
175                            .named_child(index)
176                            .expect("index below the node's own named child count"),
177                    );
178                }
179            }
180            _ => return Vec::new(),
181        }
182    }
183    if components.iter().any(|(name, _)| name.is_empty()) {
184        return Vec::new();
185    }
186    components
187}
188
189/// The historical reading of a namespace name node: its whole source text as
190/// one component, with a leading global `::` marker dropped so the caller's
191/// global-scope handling stays the AST boundary rather than a text prefix.
192///
193/// This is the recovery path for source outside the C++ grammar, so the text it
194/// returns can carry a separator that names nothing. react-native-windows
195/// templates its C++/WinRT namespaces as `namespace winrt::{{ namespaceCpp }}`,
196/// and tree-sitter stops the name node at the `{{`, leaving `winrt::` -- a
197/// trailing separator with no tail. The caller stores this component in
198/// `short_name` and derives the fq by splitting it back apart, so an empty tail
199/// desyncs the two and aborts the whole build. [`normalize_joined`] drops it
200/// here, at the one place the malformed text enters, rather than leaving each
201/// consumer to guard (#2353, a variant of #1878).
202fn cpp_raw_namespace_name_components(node: Node<'_>, source: &str) -> Vec<String> {
203    let start = node
204        .child(0)
205        .filter(|child| !child.is_named() && child.kind() == "::")
206        .map_or(node.start_byte(), |marker| marker.end_byte());
207    let text = normalize_cpp_whitespace(
208        source
209            .get(start..node.end_byte())
210            .expect("namespace name node covers one source range"),
211    );
212    let text = normalize_joined(&text, CPP_PACKAGE_SEPARATOR).into_owned();
213    if text.is_empty() {
214        return Vec::new();
215    }
216    vec![text]
217}
218
219/// The namespace definition ancestors of `node`, ordered from outermost to innermost.
220pub fn enclosing_cpp_namespace_nodes<'tree>(node: Node<'tree>) -> Vec<Node<'tree>> {
221    let mut namespaces = Vec::new();
222    let mut current = node.parent();
223    while let Some(parent) = current {
224        if parent.kind() == "namespace_definition" {
225            namespaces.push(parent);
226        }
227        current = parent.parent();
228    }
229    namespaces.reverse();
230    namespaces
231}
232
233/// Return the named namespace path that structurally encloses `node`.
234///
235/// This intentionally follows namespace AST ancestors rather than inspecting
236/// source text. Anonymous namespaces are not representable in the legacy C++
237/// package field, so a path containing one fails closed.
238fn cpp_lexical_namespace_name<'tree>(node: Node<'tree>, source: &str) -> Option<String> {
239    let mut components = Vec::new();
240    for current in enclosing_cpp_namespace_nodes(node) {
241        let name_node = current.child_by_field_name("name")?;
242        let name = normalize_cpp_whitespace(node_text(name_node, source));
243        if name.is_empty() {
244            return None;
245        }
246        components.push(name);
247    }
248    if components.is_empty() {
249        return None;
250    }
251    // Same shared empty-component decision as `cpp_push_package`, which splits
252    // this string back apart: an enclosing namespace whose own name node is
253    // malformed contributes a component carrying its own separator (#2353).
254    Some(
255        normalize_joined(
256            &components.join(CPP_PACKAGE_SEPARATOR),
257            CPP_PACKAGE_SEPARATOR,
258        )
259        .into_owned(),
260    )
261}
262
263/// Nested-class `$` join for short names. An anonymous parent class (empty
264/// short_name) contributes no segment: the FqName bridge drops empty
265/// components, so a bare `parent$child` join would desync `short_name` from
266/// the fq and trip the package/short boundary assert in
267/// `CodeUnit::with_signature_and_fq` (#2140).
268fn cpp_join_nested_short(parent_short: &str, name: &str) -> String {
269    cpp_join_short_name(parent_short, name, '$')
270}
271
272/// Member `.` join for short names; same anonymous-parent guard as
273/// [`cpp_join_nested_short`] (#2140).
274fn cpp_join_member_short(parent_short: &str, name: &str) -> String {
275    cpp_join_short_name(parent_short, name, '.')
276}
277
278fn cpp_join_short_name(parent_short: &str, name: &str, separator: char) -> String {
279    if parent_short.is_empty() {
280        name.to_string()
281    } else {
282        format!("{parent_short}{separator}{name}")
283    }
284}
285
286/// Structural fq for a leaf declaration: the parent unit's fq plus this
287/// declaration's own name as one segment (or the package segments plus the
288/// name when parentless). Never re-splits the legacy `$`/`.`-joined short
289/// chain, so a literal `$` inside a source identifier (Cython template
290/// substitution points, gcc `$`-identifiers) survives instead of corrupting
291/// the chain and tripping the package/short boundary assert (#2140).
292fn cpp_leaf_fq(
293    package_name: &str,
294    parent: Option<&CodeUnit>,
295    name: &str,
296    kind_if_nested: SegmentKind,
297    kind_if_top: SegmentKind,
298) -> FqName {
299    if let Some(parent) = parent {
300        parent
301            .fq()
302            .clone()
303            .with_pushed(segment_interner().intern(name, kind_if_nested))
304    } else {
305        let mut fq = FqName::new();
306        cpp_push_package(&mut fq, package_name);
307        fq.push(segment_interner().intern(name, kind_if_top));
308        fq
309    }
310}
311
312/// Structured name for a member unit (function, field, enumerator). The
313/// `short_name` is the owning `$`-joined nested-class `Type` chain followed, when
314/// the member has an owner, by `.member`; free functions and globals have no
315/// owner and no `.`, so the whole `short_name` is the terminal [`SegmentKind::Member`].
316/// C++ member names never contain a literal `.`, so the single `.` (if any)
317/// separates the owner chain from the member.
318pub fn cpp_member_fq(package_name: &str, short_name: &str) -> FqName {
319    let mut fq = FqName::new();
320    cpp_push_package(&mut fq, package_name);
321    match short_name.rsplit_once('.') {
322        Some((owner_chain, member)) => {
323            cpp_push_type_chain(&mut fq, owner_chain);
324            fq.push(segment_interner().intern(member, SegmentKind::Member));
325        }
326        None => fq.push(segment_interner().intern(short_name, SegmentKind::Member)),
327    }
328    fq
329}
330
331/// Structured name for a rendered C++ qualified name supplied at the input
332/// edge, such as an `Analyzer::definitions("ns.Outer$Inner")` argument.
333///
334/// The shared client-path splitter only knows `::`, `.`, `\`, `/` and `+`, so
335/// it hands back one component for a nested-class chain (`Outer$Inner`). An
336/// exact-name lookup then asks the store for owner `ns` with identifier
337/// `Outer$Inner`, while the declaration persists as a
338/// [`SegmentKind::Type`]/[`SegmentKind::Nested`] pair whose identifier is the
339/// class's own name (`Inner`). Rebuilding the `$` split from the same rule that
340/// emits it ([`cpp_push_type_chain`]) keeps the query's owner/terminal
341/// boundary identical to the persisted one. A `$` that is *not* a nesting join
342/// (gcc `$`-identifiers, #2140) cannot be told apart from this spelling here,
343/// and stays reachable through the identifier fallback, which seeks the whole
344/// component text as one spelling.
345pub fn cpp_rendered_name_fq(rendered: &str) -> FqName {
346    let mut fq = FqName::new();
347    for component in parse_symbol_path(Language::Cpp, rendered) {
348        // fqname-M4: sanctioned input-edge interning bridge: this BUILDS the FqName's
349        // Type/Nested split from the legacy `$`-joined nested-class chain, at the same
350        // construction boundary `cpp_push_type_chain` owns for extraction.
351        let mut first = true;
352        for part in component.split('$').filter(|part| !part.is_empty()) {
353            let kind = if first {
354                // A client-typed head claims no kind, exactly as
355                // `parse_symbol_path_fq` records it.
356                SegmentKind::Unknown
357            } else {
358                SegmentKind::Nested
359            };
360            fq.push(segment_interner().intern(part, kind));
361            first = false;
362        }
363    }
364    fq
365}
366
367/// Fully-kinded identities a C++ source spelling joined only by `::` can name.
368///
369/// [`parse_symbol_path`] records every component as [`SegmentKind::Unknown`],
370/// and `Unknown` re-renders with `.`. `Top::Only` therefore compares as
371/// `Top.Only`, and the exact-name retain drops the persisted nested identity
372/// `Top$Only` (`Type` + `Nested`). These interpretations are the kind
373/// placements extraction stores, so each exact query's parent and terminal
374/// match a persisted row and the structural retain keeps it.
375///
376/// A spelling that already uses `.` or `$` is a canonical rendering (#3520)
377/// and stays on [`cpp_rendered_name_fq`] alone. `base::android.ScopedJavaGlobalRef`
378/// is one of those: the `::` there is the package join the canonical name
379/// already spells.
380pub fn cpp_colon_source_aliases(rendered: &str) -> Vec<FqName> {
381    if !rendered.contains("::") || rendered.contains('.') || rendered.contains('$') {
382        return Vec::new();
383    }
384    let texts = parse_symbol_path(Language::Cpp, rendered);
385    if texts.len() < 2 {
386        return Vec::new();
387    }
388    cpp_qualified_kind_interpretations(&texts)
389}
390
391/// Every namespace / type / member boundary a `::`-joined source spelling can
392/// persist as. `packages` is the number of leading [`SegmentKind::Package`]
393/// segments.
394fn cpp_qualified_kind_interpretations(texts: &[String]) -> Vec<FqName> {
395    let len = texts.len();
396    let mut names = Vec::new();
397    names.push(cpp_fq_with_kinds(texts, vec![SegmentKind::Package; len]));
398    for packages in 0..len {
399        let rest = len - packages;
400        let mut type_chain = Vec::with_capacity(len);
401        type_chain.extend(std::iter::repeat_n(SegmentKind::Package, packages));
402        type_chain.push(SegmentKind::Type);
403        type_chain.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 1));
404        names.push(cpp_fq_with_kinds(texts, type_chain));
405        if rest >= 2 {
406            let mut member = Vec::with_capacity(len);
407            member.extend(std::iter::repeat_n(SegmentKind::Package, packages));
408            member.push(SegmentKind::Type);
409            member.extend(std::iter::repeat_n(SegmentKind::Nested, rest - 2));
410            member.push(SegmentKind::Member);
411            names.push(cpp_fq_with_kinds(texts, member));
412        } else if packages >= 1 {
413            let mut function = vec![SegmentKind::Package; packages];
414            function.push(SegmentKind::Member);
415            names.push(cpp_fq_with_kinds(texts, function));
416        }
417    }
418    names
419}
420
421fn cpp_fq_with_kinds(texts: &[String], kinds: Vec<SegmentKind>) -> FqName {
422    assert_eq!(
423        texts.len(),
424        kinds.len(),
425        "a qualified interpretation names every segment"
426    );
427    let mut fq = FqName::new();
428    for (text, kind) in texts.iter().zip(kinds) {
429        fq.push(segment_interner().intern(text, kind));
430    }
431    fq
432}
433
434#[derive(Clone)]
435pub struct ScopeInfo {
436    package_name: String,
437    module: Option<CodeUnit>,
438    class_unit: Option<CodeUnit>,
439    template_signature: Option<String>,
440    template_metadata: Option<CppTemplateMetadata>,
441    declarations_are_fields: bool,
442    recovered_specialization_member_scope: bool,
443    /// Namespace targets of every `using namespace X;` directive lexically
444    /// visible at this point in the file (declaration order), threaded
445    /// forward sibling-by-sibling by the sequential container walk (see
446    /// `CppWork::Siblings`). An out-of-line member definition written as a
447    /// bare `Class::method` at file/namespace scope with no enclosing
448    /// `namespace {}` block (issue #1093, e.g. log4cxx's
449    /// `using namespace LOG4CXX_NS; ... LogString HTMLLayout::getContentType()
450    /// const { ... }`) has no other structural signal for which namespace
451    /// actually owns `Class`; this is the best-effort candidate list used to
452    /// recover it so the definition's indexed identity matches its header
453    /// declaration's.
454    visible_using_namespaces: Vec<String>,
455    /// The block scope a C++ function-local class is written directly in.
456    /// Only the local-type walk sets it, on the scope it hands one local
457    /// class; `class_unit` takes precedence, and the class's own body scope
458    /// clears it, so members and nested classes keep their ordinary owner.
459    local_block: Option<CppLocalBlock>,
460    /// Every namespace-scope `using ns::Type;` declaration lexically visible
461    /// at this point, threaded forward like `visible_using_namespaces`. Each
462    /// scope clones this list for every sibling it visits while only a using
463    /// declaration appends to it, so the list is shared copy-on-write.
464    visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
465}
466
467/// One `using ns::Type;` declaration at namespace scope: the name it
468/// introduces and the canonical namespace of the entity it names.
469///
470/// An out-of-line member definition whose owner qualifier starts with that
471/// name (`void Type::method() {}`) defines a member of `ns::Type`; the local
472/// spelling never creates a second owner (#6902 item 4).
473#[derive(Clone, Debug, PartialEq, Eq)]
474pub struct CppUsingDeclarationBinding {
475    /// The `::`-joined package of the namespace scope that holds the using
476    /// declaration. A binding proves an owner only for definitions in this
477    /// same scope: an inner namespace may declare its own class with the same
478    /// name, which per-file extraction cannot rule out.
479    scope_package: String,
480    /// The introduced name, the target's terminal component.
481    name: String,
482    /// The `::`-joined namespace of the target.
483    target_package: String,
484}
485
486/// The lexical identity of one block in a callable body: the callable's own
487/// identity plus a `block@line:column` segment at the block's opening brace.
488/// The coordinate is what separates same-named local classes in overloads
489/// (which share the callable's FqName) and in sibling blocks, without
490/// flattening the class into its enclosing namespace.
491#[derive(Clone)]
492struct CppLocalBlock {
493    callable: CodeUnit,
494    fq: FqName,
495    short_name: String,
496}
497
498impl CppLocalBlock {
499    fn new(callable: &CodeUnit, block: Node<'_>) -> Self {
500        let position = block.start_position();
501        let segment = format!("block@{}:{}", position.row + 1, position.column + 1);
502        Self {
503            fq: callable
504                .fq()
505                .clone()
506                .with_pushed(segment_interner().intern(&segment, SegmentKind::Nested)),
507            short_name: cpp_join_nested_short(callable.short_name(), &segment),
508            callable: callable.clone(),
509        }
510    }
511}
512
513struct CppContainer<'tree> {
514    node: Node<'tree>,
515    scope: ScopeInfo,
516}
517
518struct CppNodeWork<'tree> {
519    node: Node<'tree>,
520    scope: ScopeInfo,
521}
522
523/// Cursor over one container's remaining named children, processed one at a
524/// time (rather than all at once) so a `using namespace X;` sibling can
525/// update `scope.visible_using_namespaces` for the siblings that follow it,
526/// matching real C++ using-directive semantics. Nested container work is
527/// still pushed and fully drained before the cursor resumes (stack LIFO
528/// order), preserving the original left-to-right visitation order.
529struct CppSiblingsWork<'tree> {
530    children: std::vec::IntoIter<Node<'tree>>,
531    scope: ScopeInfo,
532}
533
534enum CppWork<'tree> {
535    Container(CppContainer<'tree>),
536    Node(CppNodeWork<'tree>),
537    Siblings(CppSiblingsWork<'tree>),
538}
539
540fn class_like_name<'tree>(
541    node: Node<'tree>,
542    source: &str,
543    ancestry: &ParentIndex<'tree>,
544) -> Option<String> {
545    let best = class_like_name_from_children(node, source);
546    if let Some(parent) = ancestry.parent(node)
547        && matches!(
548            parent.kind(),
549            "declaration" | "field_declaration" | "function_definition"
550        )
551        // A class_specifier carrying its own body proves the grammar name is
552        // the real class name: a sibling declarator then declares an object
553        // (`class X {} x;`), never a displaced class name. The gate matters
554        // when the class name is itself an all-caps token (`X`, `API`) --
555        // without it the export-macro re-read below steals the object
556        // declarator's name for the class (#2283). The genuine export-macro
557        // shapes leave the class_specifier bodyless, the same invariant
558        // recover_malformed_exported_multiple_base_class already gates on.
559        && cpp_body_node(node).is_none()
560        && node
561            .child_by_field_name("name")
562            .map(|name_node| {
563                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
564            })
565            .unwrap_or(false)
566        && let Some(recovered) = exported_class_name_from_node(parent, source)
567        && best.as_deref() != Some(recovered.as_str())
568    {
569        return Some(recovered);
570    }
571    best.or_else(|| {
572        node.child_by_field_name("name")
573            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
574            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
575    })
576}
577
578fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
579    let mut grammar_name = None;
580    if let Some(name_node) = node.child_by_field_name("name") {
581        let name = normalize_cpp_whitespace(node_text(name_node, source));
582        if name.is_empty() {
583            return None;
584        }
585        if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
586            return Some(name);
587        }
588        grammar_name = Some(name);
589    }
590
591    let mut best = None;
592    let mut cursor = node.walk();
593    let mut stack = Vec::new();
594    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
595        if matches!(
596            child.kind(),
597            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
598        ) {
599            break;
600        }
601        stack.push(child);
602    }
603
604    while let Some(current) = stack.pop() {
605        // Native attributes decorate the class; their identifiers cannot name it.
606        if current.kind() == "attribute_declaration" {
607            continue;
608        }
609        if matches!(current.kind(), "type_identifier" | "identifier") {
610            let name = normalize_cpp_whitespace(node_text(current, source));
611            if !name.is_empty() && !cpp_export_macro_token(&name) {
612                best = Some(name);
613            }
614            continue;
615        }
616
617        push_named_children_reversed(current, &mut stack);
618    }
619    best.or(grammar_name)
620}
621
622pub fn cpp_export_macro_token(token: &str) -> bool {
623    token
624        .chars()
625        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
626}
627
628struct RecoveredExportedClass<'tree> {
629    declaration_node: Node<'tree>,
630    name: String,
631    body: Option<Node<'tree>>,
632    raw_supertypes: Option<Vec<String>>,
633    base_facts: Option<Vec<CppBaseSpecifierFact>>,
634    uses_initializer_body: bool,
635    /// Present only for the fragmented multiple-base export shape (issue #938).
636    /// Carries the true class-body byte region -- the members tree-sitter scattered
637    /// out of the recovered node -- so they can be reparsed and re-owned as members
638    /// rather than lost inside the truncated `initializer_list` stand-in.
639    fragmented_body: Option<FragmentedExportBody>,
640}
641
642struct RecoveredFunctionLikeExportClassPair {
643    name: String,
644    range: Range,
645    raw_supertypes: Option<Vec<String>>,
646    base_facts: Option<Vec<CppBaseSpecifierFact>>,
647    fragmented_body: FragmentedExportBody,
648}
649
650struct RecoveredEmbeddedFunctionLikeExportClass {
651    name: String,
652    range: Range,
653    raw_supertypes: Vec<String>,
654    base_facts: Vec<CppBaseSpecifierFact>,
655    fragmented_body: FragmentedExportBody,
656}
657
658struct RecoveredFunctionLikeExportClass {
659    name: String,
660    range: Range,
661    raw_supertypes: Option<Vec<String>>,
662    base_facts: Option<Vec<CppBaseSpecifierFact>>,
663    member_outcome: Option<FragmentedExportMembers>,
664}
665
666/// The recovered class-body geometry for a fragmented multiple-base export class.
667/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
668/// verbatim for a region reparse (issue #941 machinery) so every recovered member
669/// keeps its exact original byte/line position. `class_range` is the full class
670/// navigation range spanning to the displaced closing brace.
671struct FragmentedExportBody {
672    reparse_start: usize,
673    reparse_end: usize,
674    class_range: Range,
675}
676
677fn recovered_fragmented_export_body(
678    body: Node<'_>,
679    class_range: Range,
680) -> Option<FragmentedExportBody> {
681    let open = body.child(0).filter(|child| child.kind() == "{")?;
682    let close = body
683        .child(body.child_count().saturating_sub(1))
684        .filter(|child| child.kind() == "}" && !child.is_missing());
685    Some(FragmentedExportBody {
686        reparse_start: open.end_byte(),
687        // A zero-width missing `}` contributes no source byte. Keep the whole
688        // body range in that case; subtracting one byte would discard the last
689        // member's semicolon and make the otherwise valid region unsafe to
690        // index. A real close token is excluded by its structured start.
691        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
692        class_range,
693    })
694}
695
696struct DisplacedFragmentNamespaceBoundary<'tree> {
697    class_close: Node<'tree>,
698    class_semicolon: Node<'tree>,
699    namespace_items: Vec<Node<'tree>>,
700}
701
702/// Result of validating a reparsed fragmented class body.  A complete tree can
703/// safely consume the whole region.  A partial tree may contain only the exact
704/// class-named constructor that tree-sitter merged into an access label; its
705/// remaining siblings must stay on the ordinary outer walk.
706enum FragmentedExportMembers {
707    Complete(Tree),
708    ConditionalConstructor(Tree),
709}
710
711#[derive(Clone, Copy)]
712struct DisplacedMacroClassTail {
713    split_index: usize,
714    class_range: Range,
715}
716
717fn recover_exported_class_declaration<'tree>(
718    node: Node<'tree>,
719    source: &str,
720) -> Option<RecoveredExportedClass<'tree>> {
721    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
722        return Some(recovered);
723    }
724
725    let class_node = first_class_like_child(node)?;
726    if let Some(name_node) = class_node.child_by_field_name("name") {
727        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
728        if cpp_export_macro_token(&class_name) {
729            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
730            // Name declarator. Only a bare declarator can be the displaced class name;
731            // wrappers describe an object whose type merely happens to look macro-like.
732            let mut cursor = node.walk();
733            if node
734                .children_by_field_name("declarator", &mut cursor)
735                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
736            {
737                return None;
738            }
739        } else if has_direct_cpp_declarator(node) {
740            return None;
741        }
742    }
743    let name = exported_class_name_from_node(class_node, source)?;
744    Some(RecoveredExportedClass {
745        declaration_node: class_node,
746        name,
747        body: cpp_body_node(class_node),
748        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
749            .then(|| extract_cpp_supertypes(class_node, source)),
750        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
751            .then(|| extract_cpp_base_facts(class_node, source)),
752        uses_initializer_body: false,
753        fragmented_body: None,
754    })
755}
756
757fn recover_malformed_exported_base_class<'tree>(
758    node: Node<'tree>,
759    source: &str,
760) -> Option<RecoveredExportedClass<'tree>> {
761    if node.kind() != "declaration" {
762        return None;
763    }
764    let class_node = node.child_by_field_name("type")?;
765    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
766        return None;
767    }
768    let macro_name = class_node
769        .child_by_field_name("name")
770        .and_then(|name| direct_identifier_name(name, source))?;
771    if !cpp_export_macro_token(&macro_name) {
772        return None;
773    }
774
775    let mut named_cursor = node.walk();
776    let mut named = node.named_children(&mut named_cursor);
777    // Object-like macro lines before the head are provenance modifiers under
778    // the macro-declaration grammar; the class node follows them.
779    let head_matches_class = loop {
780        let child = named.next()?;
781        if child.kind() == "macro_modifier" {
782            continue;
783        }
784        break same_node(child, class_node);
785    };
786    if !head_matches_class {
787        return None;
788    }
789    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
790    if displaced.kind() == "init_declarator" {
791        // With a single base and an empty body the entire malformed head is
792        // the initializer's declarator, rather than a sibling of it.
793        if named.next().is_some() {
794            return None;
795        }
796        let declarator = displaced.child_by_field_name("declarator")?;
797        let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
798        let body = displaced.child_by_field_name("value")?;
799        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
800            return None;
801        }
802        return Some(RecoveredExportedClass {
803            declaration_node: node,
804            name,
805            body: Some(body),
806            raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
807            base_facts: Some(vec![recovered_base_specifier(
808                base,
809                source,
810                recovered_base_is_virtual(declarator, base, source),
811            )?]),
812            uses_initializer_body: true,
813            fragmented_body: fragmented_export_body_region(node, body, source),
814        });
815    }
816    if displaced.kind() == "parenthesized_declarator" {
817        // The macro-declaration grammar keeps the invocation, the displaced
818        // class name and the first base inside a `parenthesized_declarator`,
819        // and the remaining bases beside the body in the trailing
820        // `init_declarator`.
821        let (name, first_base_node) =
822            recovered_function_like_export_class_owner_node(displaced, source)?;
823        let remaining = named.collect::<Vec<_>>();
824        let [init] = remaining.as_slice() else {
825            return None;
826        };
827        if init.kind() != "init_declarator" {
828            return None;
829        }
830        let mut final_base_node = init.child_by_field_name("declarator")?;
831        while final_base_node.kind() == "macro_modifier_declarator" {
832            final_base_node = final_base_node.child_by_field_name("declarator")?;
833        }
834        let first_base = recovered_malformed_base_name(first_base_node, source)?;
835        let final_base = recovered_malformed_base_name(final_base_node, source)?;
836        let body = init.child_by_field_name("value")?;
837        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
838            return None;
839        }
840        return Some(RecoveredExportedClass {
841            declaration_node: node,
842            name,
843            body: Some(body),
844            raw_supertypes: Some(vec![first_base, final_base]),
845            base_facts: Some(vec![
846                recovered_base_specifier(
847                    first_base_node,
848                    source,
849                    recovered_base_is_virtual(displaced, first_base_node, source),
850                )?,
851                recovered_base_specifier(
852                    final_base_node,
853                    source,
854                    recovered_base_is_virtual(*init, final_base_node, source),
855                )?,
856            ]),
857            uses_initializer_body: true,
858            fragmented_body: fragmented_export_body_region(node, body, source),
859        });
860    }
861    if displaced.kind() != "ERROR" {
862        return None;
863    }
864    let name = displaced_exported_class_name(displaced, source)?;
865
866    let remaining = named.collect::<Vec<_>>();
867    let init = *remaining.last()?;
868    if init.kind() != "init_declarator" {
869        return None;
870    }
871    let final_base = init
872        .child_by_field_name("declarator")
873        .and_then(|base| recovered_malformed_base_name(base, source))?;
874    let body = init.child_by_field_name("value")?;
875    // A complete reduction has a real closing brace here. In Chromium's Widget
876    // declaration, tree-sitter instead emits the same direct `}` slot as a
877    // zero-width missing node where the first body macro truncates the prefix.
878    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
879        return None;
880    }
881
882    if remaining[..remaining.len() - 1]
883        .iter()
884        .any(|child| match child.kind() {
885            "qualified_identifier"
886            | "scoped_type_identifier"
887            | "type_identifier"
888            | "identifier"
889            | "macro_modifier_declarator" => false,
890            "ERROR" => !is_malformed_inheritance_access(*child, source),
891            _ => true,
892        })
893    {
894        return None;
895    }
896
897    let mut raw_supertypes = Vec::new();
898    let mut base_facts = Vec::new();
899    for base in &remaining[..remaining.len() - 1] {
900        if base.kind() == "ERROR" {
901            continue;
902        }
903        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
904        base_facts.push(recovered_base_specifier(
905            *base,
906            source,
907            recovered_base_is_virtual(node, *base, source),
908        )?);
909    }
910    raw_supertypes.push(final_base);
911    let final_base_node = init.child_by_field_name("declarator")?;
912    base_facts.push(recovered_base_specifier(
913        final_base_node,
914        source,
915        recovered_base_is_virtual(init, final_base_node, source),
916    )?);
917
918    Some(RecoveredExportedClass {
919        declaration_node: node,
920        name,
921        body: Some(body),
922        raw_supertypes: Some(raw_supertypes),
923        base_facts: Some(base_facts),
924        uses_initializer_body: true,
925        fragmented_body: fragmented_export_body_region(node, body, source),
926    })
927}
928
929/// Locate the true class-body region for a fragmented multiple-base export class.
930///
931/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
932/// emits in place of the real class body. Tree-sitter reduces that body in one of
933/// two shapes, both of which lose the members from the recovered node:
934///
935/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
936///   a real closing brace and holds the whole body text inline. The interior between
937///   the braces reparses to the members directly.
938/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
939///   first member with a zero-width MISSING `}`; every later member -- and the real
940///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
941///   siblings. The interior runs from the opening brace to that displaced `}`.
942///
943/// Returns the interior byte range to reparse plus the full class navigation range.
944fn fragmented_export_body_region(
945    node: Node<'_>,
946    body: Node<'_>,
947    source: &str,
948) -> Option<FragmentedExportBody> {
949    let reparse_start = body.start_byte() + 1;
950    let close = direct_close_brace(body)?;
951    if close.end_byte() > close.start_byte() {
952        return Some(FragmentedExportBody {
953            reparse_start,
954            reparse_end: close.start_byte(),
955            class_range: cpp_declaration_range(node),
956        });
957    }
958    // The closing brace was displaced past the recovered node. A balanced nested
959    // class keeps its own braces, so the first lone-`}` sibling is this class's.
960    let mut sibling = node.next_named_sibling();
961    let displaced_close = loop {
962        let Some(current) = sibling else {
963            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
964        };
965        if cpp_is_stray_close_brace(current, source) {
966            break current;
967        }
968        sibling = current.next_named_sibling();
969    };
970    Some(FragmentedExportBody {
971        reparse_start,
972        reparse_end: displaced_close.start_byte(),
973        class_range: Range {
974            start_byte: node.start_byte(),
975            end_byte: displaced_close.end_byte(),
976            start_line: node.start_position().row + 1,
977            end_line: displaced_close.end_position().row + 1,
978        },
979    })
980}
981
982/// Locate the true class-body region for the export-macro class shape that
983/// tree-sitter promotes to a `function_definition`.
984///
985/// In this shape the synthetic function body closes at the first inline
986/// method, while the class's real members continue as root-level siblings until
987/// a stray `}` followed by the displaced class `;`. Reparse the complete
988/// interior so those siblings are visited with the recovered class scope.
989fn fragmented_export_function_body_region(
990    node: Node<'_>,
991    body: Node<'_>,
992    source: &str,
993    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
994) -> Option<FragmentedExportBody> {
995    let reparse_start = body.start_byte().checked_add(1)?;
996    if let Some(boundary) = displaced_namespace {
997        return Some(FragmentedExportBody {
998            reparse_start,
999            reparse_end: boundary.class_close.start_byte(),
1000            class_range: Range {
1001                start_byte: node.start_byte(),
1002                end_byte: boundary.class_semicolon.end_byte(),
1003                start_line: node.start_position().row + 1,
1004                end_line: boundary.class_semicolon.end_position().row + 1,
1005            },
1006        });
1007    }
1008    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1009    let boundary =
1010        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
1011    let boundary_index = boundary.and_then(|boundary| {
1012        siblings
1013            .iter()
1014            .position(|candidate| same_node(*candidate, boundary))
1015    });
1016    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
1017    let mut sibling_index = 0;
1018    // A complete recovered class's synthetic wrapper is immediately followed
1019    // by its displaced semicolon (comments and a trailing attribute macro --
1020    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
1021    // may sit between the body and that semicolon). Only scan for a later
1022    // stray close when real member siblings intervene; otherwise every earlier
1023    // complete class would borrow the next malformed class's close and claim
1024    // its members. The trailing-attribute case is the gtest shape: the scan
1025    // borrowed a close ~1900 lines later and re-owned a following
1026    // `namespace testing { namespace internal {` block as class members,
1027    // doubling the package path ("testing::internal::testing::internal") and
1028    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
1029    // boundary assert (#2297).
1030    while let Some(current) = siblings.get(sibling_index).copied() {
1031        if current.kind() == "comment" {
1032            sibling_index += 1;
1033            continue;
1034        }
1035        if is_trailing_attribute_macro_sibling(current) {
1036            sibling_index += 1;
1037            continue;
1038        }
1039        if cpp_is_stray_semicolon(current, source) {
1040            return None;
1041        }
1042        break;
1043    }
1044    while let Some(current) = siblings.get(sibling_index).copied() {
1045        let next = siblings.get(sibling_index + 1).copied();
1046        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1047            return Some(FragmentedExportBody {
1048                reparse_start,
1049                reparse_end: current.start_byte(),
1050                class_range: Range {
1051                    start_byte: node.start_byte(),
1052                    end_byte: semicolon.end_byte(),
1053                    start_line: node.start_position().row + 1,
1054                    end_line: semicolon.end_position().row + 1,
1055                },
1056            });
1057        }
1058        // When the final access label keeps the class close in its malformed
1059        // declaration body, tree-sitter nests the lone `}` ERROR below the
1060        // label instead of exposing it as a direct sibling. Search only the
1061        // scattered siblings after the synthetic wrapper. The first such
1062        // close is the class terminator because nested class bodies retain
1063        // their own balanced class_specifier nodes.
1064        if current.start_byte() >= body.end_byte()
1065            && let Some(close) = cpp_nested_stray_close_brace(current, source)
1066        {
1067            return Some(FragmentedExportBody {
1068                reparse_start,
1069                reparse_end: close.start_byte(),
1070                class_range: Range {
1071                    start_byte: node.start_byte(),
1072                    end_byte: current.end_byte(),
1073                    start_line: node.start_position().row + 1,
1074                    end_line: current.end_position().row + 1,
1075                },
1076            });
1077        }
1078        sibling_index += 1;
1079    }
1080    boundary.map(|boundary| FragmentedExportBody {
1081        reparse_start,
1082        reparse_end: boundary.start_byte(),
1083        class_range: Range {
1084            start_byte: node.start_byte(),
1085            end_byte: boundary.start_byte(),
1086            start_line: node.start_position().row + 1,
1087            end_line: boundary.start_position().row + 1,
1088        },
1089    })
1090}
1091
1092/// Find a later macro-export class that tree-sitter lifted through an enclosing
1093/// preprocessor container. A class that is still a direct sibling can be a
1094/// nested member of the current fragmented class, so only a changed parent is
1095/// a proven boundary between the two recovered class envelopes.
1096fn fragmented_export_sibling_class_boundary<'tree>(
1097    node: Node<'tree>,
1098    source: &str,
1099    ancestry: &ParentIndex<'tree>,
1100) -> Option<Node<'tree>> {
1101    let node_parent = ancestry.parent(node)?;
1102    cpp_following_named_siblings(node, source, ancestry)
1103        .into_iter()
1104        .find(|candidate| {
1105            recover_exported_class_function_definition(*candidate, source).is_some()
1106                && ancestry
1107                    .parent(*candidate)
1108                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1109        })
1110}
1111
1112/// A trailing attribute macro after a recovered class's closing brace, spelled
1113/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
1114/// bare identifier is never a class member (members need a type), so this
1115/// sibling can only be the class's own tail (#2297).
1116fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1117    if node.kind() != "expression_statement" {
1118        return false;
1119    }
1120    let mut cursor = node.walk();
1121    let mut children = node.named_children(&mut cursor);
1122    children
1123        .next()
1124        .is_some_and(|child| child.kind() == "identifier")
1125        && children.next().is_none()
1126}
1127
1128/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
1129/// export-class wrapper can place the class close inside an access-label node,
1130/// so direct-sibling checks alone miss the boundary.  Walk named CST children
1131/// only; the helper does not inspect source text beyond the existing structured
1132/// stray-brace predicate.
1133fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1134    let mut stack = vec![node];
1135    while let Some(current) = stack.pop() {
1136        if cpp_is_stray_close_brace(current, source) {
1137            return Some(current);
1138        }
1139        let mut cursor = current.walk();
1140        stack.extend(current.named_children(&mut cursor));
1141    }
1142    None
1143}
1144
1145/// Return named siblings that follow `node`, including siblings that tree-sitter
1146/// attached to an enclosing container after malformed recovery split the local
1147/// declaration list. Stop at the first structurally visible class close so a
1148/// later namespace or exported class cannot supply the recovery boundary.
1149///
1150/// The climb takes a [`ParentIndex`] because it runs once per visited node and
1151/// walks every level to the root: with `Node::parent`, which re-descends from
1152/// the root on each call, a translation unit holding thousands of siblings --
1153/// generated instruction tables, for instance -- made this the whole cost of
1154/// indexing the file. Callers inside the declaration walk pass the walk's
1155/// index; the four that ask from outside a traversal pass
1156/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
1157/// behaviour is unchanged.
1158fn cpp_following_named_siblings<'tree>(
1159    node: Node<'tree>,
1160    source: &str,
1161    ancestry: &ParentIndex<'tree>,
1162) -> Vec<Node<'tree>> {
1163    let mut siblings = Vec::new();
1164    let mut anchor = node;
1165    while let Some(parent) = ancestry.parent(anchor) {
1166        let at_translation_unit = parent.kind() == "translation_unit";
1167        let mut cursor = parent.walk();
1168        assert!(
1169            cursor.goto_first_child(),
1170            "ParentIndex returned a parent without children"
1171        );
1172        while !same_node(cursor.node(), anchor) {
1173            assert!(
1174                cursor.goto_next_sibling(),
1175                "ParentIndex parent does not contain its child anchor"
1176            );
1177        }
1178        while cursor.goto_next_sibling() {
1179            let current = cursor.node();
1180            if !current.is_named() {
1181                continue;
1182            }
1183            if at_translation_unit
1184                && (current.kind() == "namespace_definition"
1185                    || (current.kind() == "function_definition"
1186                        && first_class_like_child(current).is_some()))
1187            {
1188                return siblings;
1189            }
1190            siblings.push(current);
1191            if cpp_is_stray_close_brace(current, source) {
1192                if let Some(semicolon) = current
1193                    .next_named_sibling()
1194                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1195                {
1196                    siblings.push(semicolon);
1197                }
1198                return siblings;
1199            }
1200            if current.start_byte() >= node.end_byte()
1201                && matches!(current.kind(), "ERROR" | "labeled_statement")
1202                && cpp_nested_stray_close_brace(current, source).is_some()
1203            {
1204                return siblings;
1205            }
1206        }
1207        anchor = parent;
1208    }
1209    siblings
1210}
1211
1212fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1213    if node.start_byte() >= class_end {
1214        return false;
1215    }
1216    node.end_byte() <= class_end
1217        || cpp_nested_stray_close_brace(node, source)
1218            .is_some_and(|close| close.start_byte() == class_end)
1219}
1220
1221/// A class whose head tree-sitter kept in one malformed container while the
1222/// members after the first and the closing `};` scattered across that
1223/// container's following siblings.
1224struct FragmentedClassRecovery<'tree> {
1225    declaration_node: Node<'tree>,
1226    name: String,
1227    raw_supertypes: Vec<String>,
1228    body: FragmentedExportBody,
1229}
1230
1231/// Recover a class whose opening prefix is retained in one ERROR node while one
1232/// or more nested class closes and the outer close are displaced to sibling
1233/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1234/// tokens establish nesting depth and the displaced close nodes terminate it.
1235///
1236/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1237/// `class` keyword as the container's first token. A function-like export macro
1238/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1239/// body-less `class_specifier` named after the macro followed by the
1240/// invocation's `(`, arguments and `)`, with the real class name and the base
1241/// clause left as bare tokens after it (#2924).
1242fn fragmented_class_body<'tree>(
1243    node: Node<'tree>,
1244    source: &str,
1245) -> Option<FragmentedClassRecovery<'tree>> {
1246    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1247        return Some(recovered);
1248    }
1249    let supported_container = node.kind() == "ERROR"
1250        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1251    if !supported_container {
1252        return None;
1253    }
1254    let mut cursor = node.walk();
1255    let children = node.children(&mut cursor).collect::<Vec<_>>();
1256    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1257        return Some(recovered);
1258    }
1259    let keyword = children.first()?;
1260    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1261        return None;
1262    }
1263    let name_node = children
1264        .iter()
1265        .copied()
1266        .skip(1)
1267        .find(|child| child.is_named())?;
1268    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1269        return None;
1270    }
1271    let name = normalize_cpp_whitespace(node_text(name_node, source));
1272    if name.is_empty() || cpp_export_macro_token(&name) {
1273        return None;
1274    }
1275    let open_index = children.iter().position(|child| child.kind() == "{")?;
1276    let open = children[open_index];
1277    let body = displaced_fragment_namespace_geometry(node, source)
1278        .map(|boundary| FragmentedExportBody {
1279            reparse_start: open.end_byte(),
1280            reparse_end: boundary.class_close.start_byte(),
1281            class_range: Range {
1282                start_byte: node.start_byte(),
1283                end_byte: boundary.class_semicolon.end_byte(),
1284                start_line: node.start_position().row + 1,
1285                end_line: boundary.class_semicolon.end_position().row + 1,
1286            },
1287        })
1288        .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1289    Some(FragmentedClassRecovery {
1290        declaration_node: node,
1291        name,
1292        raw_supertypes: extract_cpp_supertypes(node, source),
1293        body,
1294    })
1295}
1296
1297/// The fragmented body of a function-like export-macro class head that
1298/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1299/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1300/// public virtual EC_PublicKey {`, #2924).
1301///
1302/// The class name is read positionally by [`recovered_export_head_name`] -- the
1303/// last identifier before the head ends at `final`, at the base clause `:`, or
1304/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1305/// spelled with `virtual`, with `final`, or with qualified base names needs no
1306/// arm of its own.
1307fn fragmented_export_macro_class_body<'tree>(
1308    node: Node<'tree>,
1309    children: &[Node<'tree>],
1310    source: &str,
1311) -> Option<FragmentedClassRecovery<'tree>> {
1312    let class_node = *children.first()?;
1313    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1314        return None;
1315    }
1316    // The identifier tree-sitter took for the class name is the export macro, a
1317    // function-like invocation when the `(` of its argument list follows it
1318    // directly. Spelling plays no part (#2557).
1319    class_node
1320        .child_by_field_name("name")
1321        .and_then(|name| direct_identifier_name(name, source))?;
1322    let invocation = *children.get(1)?;
1323    if invocation.is_named() || invocation.kind() != "(" {
1324        return None;
1325    }
1326    let open_index = children
1327        .iter()
1328        .position(|child| !child.is_named() && child.kind() == "{")?;
1329    let open = children[open_index];
1330    let name_node = recovered_export_head_name(node, open, source)?;
1331    let name = normalize_cpp_whitespace(node_text(name_node, source));
1332    if name.is_empty() {
1333        return None;
1334    }
1335    Some(FragmentedClassRecovery {
1336        declaration_node: node,
1337        name,
1338        raw_supertypes: recovered_export_head_bases(
1339            node,
1340            name_node.end_byte(),
1341            open.start_byte(),
1342            source,
1343        ),
1344        body: fragmented_displaced_class_body(node, children, open_index, source)
1345            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1346    })
1347}
1348
1349/// The class body a fragmented head opened at `children[open_index]` but could
1350/// not close: tree-sitter scattered the remaining members and the closing `};`
1351/// across the container's following siblings. The direct class tokens after the
1352/// opening brace say how many closes the nested classes consume before this
1353/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1354fn fragmented_displaced_class_body(
1355    node: Node<'_>,
1356    children: &[Node<'_>],
1357    open_index: usize,
1358    source: &str,
1359) -> Option<FragmentedExportBody> {
1360    let open = children[open_index];
1361    let nested_class_opens = children[open_index + 1..]
1362        .iter()
1363        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1364        .count();
1365    let mut closes_remaining = 1 + nested_class_opens;
1366    let mut sibling = node.next_named_sibling();
1367    while let Some(candidate) = sibling {
1368        let next = candidate.next_named_sibling();
1369        // The class's own close: a lone `}` `ERROR` the mis-parse split off,
1370        // or -- under the macro-declaration grammar, which keeps the fragmented
1371        // head in an `ERROR` and the close as a plain child of the enclosing
1372        // list -- a bare `}` there. The `;` after it is what proves the close
1373        // is the class's own rather than the enclosing scope's.
1374        let is_close = cpp_is_stray_close_brace(candidate, source)
1375            || (candidate.kind() == "}" && !candidate.is_named());
1376        if is_close {
1377            closes_remaining -= 1;
1378            if closes_remaining == 0 {
1379                let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1380                if open.end_byte() >= candidate.start_byte() {
1381                    return None;
1382                }
1383                return Some(FragmentedExportBody {
1384                    reparse_start: open.end_byte(),
1385                    reparse_end: candidate.start_byte(),
1386                    class_range: Range {
1387                        start_byte: node.start_byte(),
1388                        end_byte: semicolon.end_byte(),
1389                        start_line: node.start_position().row + 1,
1390                        end_line: semicolon.end_position().row + 1,
1391                    },
1392                });
1393            }
1394        }
1395        sibling = next;
1396    }
1397    None
1398}
1399
1400/// The body of a fragmented export-macro class head whose close tree-sitter
1401/// spent on the enclosing container instead: Botan's
1402/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1403/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1404/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1405/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1406/// (#2924).
1407fn fragmented_container_close_class_body(
1408    node: Node<'_>,
1409    open: Node<'_>,
1410    source: &str,
1411) -> Option<FragmentedExportBody> {
1412    let parent = node.parent()?;
1413    if !matches!(
1414        parent.kind(),
1415        "declaration_list" | "field_declaration_list" | "compound_statement"
1416    ) {
1417        return None;
1418    }
1419    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1420    // The container's close is this class's close only when the body's own
1421    // brace balance says so. Without that cross-check -- the one issue #1524
1422    // already uses on a mis-closed wrapper body -- a class that really does end
1423    // earlier would claim every later container-level declaration as a member.
1424    if OrphanedNamespaceScopeIndex::build(parent, source)
1425        .matching_close_brace(open.start_byte())
1426        .map(|close| close.start_byte)
1427        != Some(close.start_byte())
1428        || open.end_byte() >= close.start_byte()
1429    {
1430        return None;
1431    }
1432    Some(FragmentedExportBody {
1433        reparse_start: open.end_byte(),
1434        reparse_end: close.start_byte(),
1435        class_range: Range {
1436            start_byte: node.start_byte(),
1437            end_byte: close.end_byte(),
1438            start_line: node.start_position().row + 1,
1439            end_line: close.end_position().row + 1,
1440        },
1441    })
1442}
1443
1444pub(crate) fn recovered_fragmented_class_has_body(
1445    node: Node<'_>,
1446    source: &str,
1447    expected_name: &str,
1448    expected_range: &Range,
1449) -> bool {
1450    fragmented_class_body(node, source).is_some_and(|recovered| {
1451        recovered.name == expected_name
1452            && recovered.body.class_range.start_byte == expected_range.start_byte
1453            && recovered.body.class_range.end_byte == expected_range.end_byte
1454    })
1455}
1456
1457/// Recover a plain class whose parser-visible body ends inside a malformed
1458/// inline member. Tree-sitter then attaches either the next real member
1459/// declarator or the unfinished `else` branch directly to the outer function
1460/// definition and leaves the class's actual `};` among later siblings. Those
1461/// structured continuations and the close/semicolon siblings establish the
1462/// complete body envelope without interpreting source text.
1463fn fragmented_plain_class_declaration_body<'tree>(
1464    node: Node<'tree>,
1465    source: &str,
1466) -> Option<FragmentedClassRecovery<'tree>> {
1467    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1468        return None;
1469    }
1470    let class_node = node.child_by_field_name("type")?;
1471    if !matches!(
1472        class_node.kind(),
1473        "class_specifier" | "struct_specifier" | "union_specifier"
1474    ) {
1475        return None;
1476    }
1477    let name_node = class_node.child_by_field_name("name")?;
1478    let name = normalize_cpp_whitespace(node_text(name_node, source));
1479    if name.is_empty() || cpp_export_macro_token(&name) {
1480        return None;
1481    }
1482    let body = cpp_body_node(class_node)?;
1483    if body.kind() != "field_declaration_list" {
1484        return None;
1485    }
1486    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1487        if declarator.start_byte() < class_node.end_byte() {
1488            return None;
1489        }
1490        let mut cursor = node.walk();
1491        node.named_children(&mut cursor).any(|child| {
1492            if child.kind() != "ERROR"
1493                || child.start_byte() < class_node.end_byte()
1494                || child.end_byte() > declarator.start_byte()
1495            {
1496                return false;
1497            }
1498            let mut cursor = child.walk();
1499            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1500            let Some((return_type, attributes)) = components.split_last() else {
1501                return false;
1502            };
1503            matches!(
1504                return_type.kind(),
1505                "identifier"
1506                    | "type_identifier"
1507                    | "primitive_type"
1508                    | "decltype"
1509                    | "placeholder_type_specifier"
1510            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1511                && attributes.iter().all(|attribute| {
1512                    matches!(attribute.kind(), "identifier" | "type_identifier")
1513                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1514                            *attribute, source,
1515                        )))
1516                })
1517        })
1518    } else {
1519        let mut cursor = node.walk();
1520        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1521        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1522            if same_node(*candidate_class, class_node)
1523                && continuation.kind() == "identifier"
1524                && node_text(*continuation, source) == "else"
1525                && continuation_body.kind() == "compound_statement"
1526                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1527                && continuation_body
1528                    .child(continuation_body.child_count().saturating_sub(1))
1529                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1530    };
1531    if !displaced_member {
1532        return None;
1533    }
1534    let open = body
1535        .children(&mut body.walk())
1536        .find(|child| child.kind() == "{")?;
1537    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1538    let ordinary_boundary =
1539        siblings
1540            .iter()
1541            .copied()
1542            .enumerate()
1543            .find_map(|(close_index, close)| {
1544                cpp_stray_close_followed_by_semicolon(
1545                    close,
1546                    siblings.get(close_index + 1).copied(),
1547                    source,
1548                )
1549                .map(|semicolon| (close, semicolon))
1550            });
1551    let (close, semicolon) =
1552        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1553            (boundary.class_close, boundary.class_semicolon)
1554        } else {
1555            ordinary_boundary?
1556        };
1557    if open.end_byte() >= close.start_byte() {
1558        return None;
1559    }
1560    Some(FragmentedClassRecovery {
1561        declaration_node: class_node,
1562        name,
1563        raw_supertypes: extract_cpp_supertypes(class_node, source),
1564        body: FragmentedExportBody {
1565            reparse_start: open.end_byte(),
1566            reparse_end: close.start_byte(),
1567            class_range: Range {
1568                start_byte: class_node.start_byte(),
1569                end_byte: semicolon.end_byte(),
1570                start_line: class_node.start_position().row + 1,
1571                end_line: semicolon.end_position().row + 1,
1572            },
1573        },
1574    })
1575}
1576
1577fn displaced_export_function_namespace_shape<'tree>(
1578    declaration: Node<'tree>,
1579    source: &str,
1580) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1581    let mut nested = Vec::new();
1582    for index in (0..declaration.named_child_count()).rev() {
1583        nested.push(declaration.named_child(index)?);
1584    }
1585    while let Some(current) = nested.pop() {
1586        // A recovered export class nested in this class can consume the first
1587        // parser-visible namespace close itself. In that shape the existing
1588        // later-class boundary logic already distinguishes the nested and
1589        // namespace-sibling owners; do not mistake the nested close for this
1590        // class's terminator.
1591        if recover_exported_class_function_definition(current, source).is_some() {
1592            return None;
1593        }
1594        for index in (0..current.named_child_count()).rev() {
1595            nested.push(current.named_child(index)?);
1596        }
1597    }
1598    let mut same_envelope_sibling = declaration.next_named_sibling();
1599    while let Some(current) = same_envelope_sibling {
1600        if recover_exported_class_function_definition(current, source).is_some() {
1601            return None;
1602        }
1603        same_envelope_sibling = current.next_named_sibling();
1604    }
1605    let declaration_list = declaration.parent()?;
1606    let (namespace, class_close, trailing_semicolon) =
1607        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1608    // A chain of malformed export classes can consume one parser-visible
1609    // namespace close per class. Walk through the enclosing sibling levels so
1610    // the later real namespace close remains the structural boundary; a
1611    // direct next-sibling walk stops at the first collapsed namespace and
1612    // incorrectly makes its intervening items members of this class.
1613    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1614    let trailing_index = siblings
1615        .iter()
1616        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1617    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1618        recover_exported_class_function_definition(*candidate, source).is_some()
1619    }) {
1620        // Consecutive recovered classes already have an exact sibling-class
1621        // boundary. Preserve that established path, including nested export
1622        // classes, instead of interpreting the first class close as a
1623        // collapsed namespace boundary.
1624        return None;
1625    }
1626    let mut namespace_items = Vec::new();
1627    let mut nested_fragment_end = 0;
1628    for current in siblings.into_iter().skip(trailing_index + 1) {
1629        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1630        {
1631            return Some(DisplacedFragmentNamespaceBoundary {
1632                class_close,
1633                class_semicolon: trailing_semicolon,
1634                namespace_items,
1635            });
1636        }
1637        if current.start_byte() >= nested_fragment_end
1638            && let Some(recovered) = fragmented_class_body(current, source)
1639        {
1640            nested_fragment_end = recovered.body.class_range.end_byte;
1641        } else if current.start_byte() >= nested_fragment_end
1642            && recover_exported_class_function_definition(current, source).is_some()
1643            && let Some(body) = cpp_body_node(current)
1644            && let Some(fragmented) =
1645                fragmented_export_function_body_region(current, body, source, None)
1646        {
1647            nested_fragment_end = fragmented.class_range.end_byte;
1648        }
1649        namespace_items.push(current);
1650    }
1651    None
1652}
1653
1654fn displaced_fragment_namespace_boundary<'tree>(
1655    declaration: Node<'tree>,
1656    body: Node<'tree>,
1657    source: &str,
1658) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1659    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1660    let reparse_start = body.start_byte() + 1;
1661    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1662    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1663}
1664
1665/// Recover the class/namespace brace geometry for a declaration whose class
1666/// close tree-sitter consumed as the enclosing namespace close. This proof is
1667/// independent of whether every member in the class body can be reparsed: the
1668/// ordinary-tree fallback can still re-own bounded sibling declarations when
1669/// an unknown macro makes the complete body reparse unsafe.
1670fn displaced_fragment_namespace_geometry<'tree>(
1671    declaration: Node<'tree>,
1672    source: &str,
1673) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1674    // A templated class's malformed function wrapper remains beneath the
1675    // template node even though its later members have escaped to the
1676    // enclosing declaration list. Lift only that exact declaration child.
1677    let envelope = declaration
1678        .parent()
1679        .filter(|parent| {
1680            parent.kind() == "template_declaration"
1681                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1682        })
1683        .unwrap_or(declaration);
1684    let declaration_list = envelope.parent()?;
1685    let (_, class_close, trailing_semicolon) =
1686        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1687    let mut namespace_items = Vec::new();
1688    let mut sibling = trailing_semicolon.next_named_sibling();
1689    let mut nested_fragment_end = 0;
1690    loop {
1691        let current = sibling?;
1692        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1693        {
1694            break;
1695        }
1696        if current.start_byte() >= nested_fragment_end
1697            && let Some(recovered) = fragmented_class_body(current, source)
1698        {
1699            nested_fragment_end = recovered.body.class_range.end_byte;
1700        }
1701        namespace_items.push(current);
1702        sibling = current.next_named_sibling();
1703    }
1704    Some(DisplacedFragmentNamespaceBoundary {
1705        class_close,
1706        class_semicolon: trailing_semicolon,
1707        namespace_items,
1708    })
1709}
1710
1711fn cpp_fragment_namespace_boundary_nodes<'tree>(
1712    declaration_list: Node<'tree>,
1713) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1714    if declaration_list.kind() != "declaration_list" {
1715        return None;
1716    }
1717    let namespace = declaration_list.parent()?;
1718    if namespace.kind() != "namespace_definition"
1719        || namespace.child_by_field_name("body") != Some(declaration_list)
1720    {
1721        return None;
1722    }
1723    let class_close = direct_close_brace(declaration_list)?;
1724    let trailing_semicolon = namespace.next_named_sibling()?;
1725    (trailing_semicolon.kind() == "expression_statement"
1726        && trailing_semicolon.named_child_count() == 0)
1727        .then_some((namespace, class_close, trailing_semicolon))
1728}
1729
1730/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1731fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1732    (0..node.child_count())
1733        .filter_map(|index| node.child(index))
1734        .find(|child| !child.is_named() && child.kind() == "}")
1735}
1736
1737/// A displaced lone closing brace: the class close that the fragmented multiple-base
1738/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1739fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1740    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1741}
1742
1743fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1744    let mut name = None;
1745    let mut colon_count = 0;
1746    let mut access_count = 0;
1747    for index in 0..node.child_count() {
1748        let child = node.child(index)?;
1749        match child.kind() {
1750            "identifier" | "type_identifier" if child.is_named() => {
1751                if name.is_some() {
1752                    return None;
1753                }
1754                let candidate = normalize_cpp_whitespace(node_text(child, source));
1755                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1756                    return None;
1757                }
1758                name = Some(candidate);
1759            }
1760            "template_function" | "template_type" if child.is_named() => {
1761                if name.is_some() {
1762                    return None;
1763                }
1764                let candidate = child
1765                    .child_by_field_name("name")
1766                    .and_then(|name| direct_identifier_name(name, source))?;
1767                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1768                    return None;
1769                }
1770                name = Some(candidate);
1771            }
1772            ":" if !child.is_named() => colon_count += 1,
1773            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1774            _ => return None,
1775        }
1776    }
1777    (colon_count == 1 && access_count == 1)
1778        .then_some(name)
1779        .flatten()
1780}
1781
1782fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1783    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1784        return false;
1785    }
1786    node.named_child(0)
1787        .and_then(|child| direct_identifier_name(child, source))
1788        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1789}
1790
1791use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1792
1793fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1794    match node.kind() {
1795        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1796            recovered_base_atom(node, source)
1797        }
1798        // The macro-declaration grammar wraps an access keyword and the base
1799        // as a `macro_modifier_declarator`; the base is its declarator.
1800        "macro_modifier_declarator" => node
1801            .child_by_field_name("declarator")
1802            .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1803        "template_type" | "template_function" => node
1804            .child_by_field_name("name")
1805            .and_then(|name| recovered_malformed_base_name(name, source)),
1806        "ERROR" => None,
1807        "qualified_identifier" | "scoped_type_identifier" => {
1808            let suffix = node
1809                .child_by_field_name("name")
1810                .and_then(|name| recovered_malformed_base_name(name, source))?;
1811            let scope = node
1812                .child_by_field_name("scope")
1813                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1814            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1815                malformed_qualified_prefix(node, source)?
1816            } else {
1817                if malformed_qualified_prefix(node, source).is_some() {
1818                    return None;
1819                }
1820                scope
1821            };
1822            Some(format!("{prefix}::{suffix}"))
1823        }
1824        _ => None,
1825    }
1826}
1827
1828fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1829    let mut pending = vec![node];
1830    let mut components = Vec::new();
1831    while let Some(current) = pending.pop() {
1832        match current.kind() {
1833            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1834                components.push(recovered_base_atom(current, source)?);
1835            }
1836            "macro_modifier_declarator" => {
1837                pending.push(current.child_by_field_name("declarator")?);
1838            }
1839            "template_type" | "template_function" => {
1840                pending.push(current.child_by_field_name("name")?);
1841            }
1842            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1843                pending.push(current.child_by_field_name("name")?);
1844                if let Some(scope) = current.child_by_field_name("scope") {
1845                    // Malformed export bases can put the access keyword in
1846                    // `scope` and the real scope in the direct ERROR child.
1847                    // Keep the exact AST recovery used by the declaration.
1848                    if recovered_base_atom(scope, source).is_some_and(|name| {
1849                        matches!(name.as_str(), "public" | "protected" | "private")
1850                    }) {
1851                        pending.push(malformed_qualified_prefix_node(current, source)?);
1852                    } else {
1853                        if malformed_qualified_prefix_node(current, source).is_some() {
1854                            return None;
1855                        }
1856                        pending.push(scope);
1857                    }
1858                }
1859            }
1860            _ => return None,
1861        }
1862    }
1863    Some(components)
1864}
1865
1866fn recovered_base_specifier(
1867    node: Node<'_>,
1868    source: &str,
1869    is_virtual: bool,
1870) -> Option<CppBaseSpecifierFact> {
1871    let components = recovered_base_components(node, source)?;
1872    crate::source_facts::base_specifier_fact(
1873        node,
1874        components,
1875        is_virtual,
1876        CppAccessSpecifier::Unknown,
1877    )
1878}
1879
1880fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1881    // Recovery can put the qualifier in an ERROR sibling or inside the base
1882    // node. Read only the AST prefix through this base, resetting at preceding
1883    // inheritance delimiters so another base's qualifier cannot leak here.
1884    let mut pending = vec![container];
1885    let mut is_virtual = false;
1886    while let Some(node) = pending.pop() {
1887        if node.start_byte() >= base.end_byte() {
1888            break;
1889        }
1890        if node.child_count() != 0 {
1891            let mut cursor = node.walk();
1892            let children = node
1893                .children(&mut cursor)
1894                .take_while(|child| child.start_byte() < base.end_byte())
1895                .collect::<Vec<_>>();
1896            pending.extend(children.into_iter().rev());
1897            continue;
1898        }
1899        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1900            is_virtual = false;
1901        } else if node.kind() == "virtual"
1902            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1903        {
1904            is_virtual = true;
1905        }
1906    }
1907    is_virtual
1908}
1909
1910fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1911    if !matches!(
1912        node.kind(),
1913        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1914    ) {
1915        return None;
1916    }
1917    let name = normalize_cpp_whitespace(node_text(node, source));
1918    (!name.is_empty()).then_some(name)
1919}
1920
1921fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1922    malformed_qualified_prefix_node(node, source)
1923        .and_then(|prefix| recovered_base_atom(prefix, source))
1924}
1925
1926fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1927    let mut prefix = None;
1928    let mut cursor = node.walk();
1929    for error in node
1930        .named_children(&mut cursor)
1931        .filter(|child| child.kind() == "ERROR")
1932    {
1933        if prefix.is_some() {
1934            return None;
1935        }
1936        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1937        // the qualified name's scope and leaves `virtual` beside the real
1938        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1939        // component of the base's name (#2924).
1940        let mut error_cursor = error.walk();
1941        let atoms = error
1942            .named_children(&mut error_cursor)
1943            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1944            .collect::<Option<Vec<_>>>()?;
1945        let [atom] = atoms
1946            .iter()
1947            .filter(|(_, name)| name != "virtual")
1948            .collect::<Vec<_>>()[..]
1949        else {
1950            return None;
1951        };
1952        prefix = Some(atom.0);
1953    }
1954    prefix
1955}
1956
1957type RecoveredFunctionDefinitionClass<'tree> = (
1958    Node<'tree>,
1959    String,
1960    Option<Vec<String>>,
1961    Option<Vec<CppBaseSpecifierFact>>,
1962);
1963/// One declaration an attribute-like macro invocation swallowed into a
1964/// declaration-scope `ERROR`, with the byte range that spells it.
1965/// What [`stranded_declaration_run`] read out of one node.
1966struct StrandedRun<'tree> {
1967    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1968    /// Whether every part of the node read as part of a declaration. False when
1969    /// a part the reader does not understand ended it early, or when the last
1970    /// parts were types with no declarator after them. Callers that index what
1971    /// was found keep the declarations either way; a caller deciding whether a
1972    /// whole region is safe to index requires this.
1973    complete: bool,
1974}
1975
1976struct MacroWrappedDeclaration<'tree> {
1977    declarator: Node<'tree>,
1978    range: Range,
1979    /// Whether the recovered declaration spells `static`. The envelope hides
1980    /// that keyword from the ordinary linkage reader, which looks for it among
1981    /// a declaration node's own children, and internal linkage is what decides
1982    /// whether a header declaration and a body in another file are one symbol.
1983    is_static: bool,
1984}
1985
1986/// Whether `node` stands where declarations live: directly in the translation
1987/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1988/// stands in one of those.
1989///
1990/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1991/// where it stands. The case that matters is a whole translation unit the
1992/// parser could not close: whisper.cpp's `include/whisper.h` opens
1993/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1994/// so the braces never balance in the token stream tree-sitter sees, the file's
1995/// own root node is an `ERROR`, and every top-level declaration in the file is
1996/// a child of it (#3094). Without this the macro recoveries below read nothing
1997/// in such a file, because none of their envelopes is in a declaration scope
1998/// the parser managed to build.
1999fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2000    declaration_scope_container(node, ancestry).is_some()
2001}
2002
2003/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
2004/// end is where a parse failure that starts at `node` can still be doing
2005/// damage: a declaration scope has no synchronization point of its own, so the
2006/// parser carries such a failure to the scope's close.
2007fn declaration_scope_container<'tree>(
2008    node: Node<'tree>,
2009    ancestry: &ParentIndex<'tree>,
2010) -> Option<Node<'tree>> {
2011    let mut parent = ancestry.parent(node)?;
2012    loop {
2013        match parent.kind() {
2014            "translation_unit" => return Some(parent),
2015            "declaration_list" => {
2016                return ancestry
2017                    .parent(parent)
2018                    .is_some_and(|grandparent| {
2019                        matches!(
2020                            grandparent.kind(),
2021                            "namespace_definition" | "linkage_specification"
2022                        )
2023                    })
2024                    .then_some(parent);
2025            }
2026            "ERROR" => match ancestry.parent(parent) {
2027                Some(grandparent) => parent = grandparent,
2028                // A root `ERROR` is the translation unit the parser could not
2029                // build, so its children stand at file scope.
2030                None => return Some(parent),
2031            },
2032            // A preprocessor conditional is a transparent declaration-scope
2033            // wrapper when it sits directly under the translation unit or a
2034            // namespace/linkage declaration list. Keep climbing through it;
2035            // the next ordinary construct still decides whether the enclosed
2036            // error is at declaration scope. In particular, a conditional
2037            // inside a function reaches its compound statement and is
2038            // rejected below rather than becoming a global recovery region.
2039            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2040                parent = ancestry.parent(parent)?;
2041            }
2042            _ => return None,
2043        }
2044    }
2045}
2046
2047/// Whether `node` is an `ERROR` the parser produced where declarations live.
2048fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2049    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2050}
2051
2052/// The identifier an object-like macro marker wraps. The grammar admits a lone
2053/// identifier as a `macro_modifier` in a declaration's specifier run, or as
2054/// the prefix of a `macro_modifier_declarator`, only where the declaration
2055/// would otherwise not parse, so the modifier is always the structured reading
2056/// of a macro the workspace never expands: it is provenance about the
2057/// declaration, never a declarator of its own.
2058pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2059    if node.kind() != "macro_modifier" {
2060        return None;
2061    }
2062    node.named_child(0)
2063        .filter(|child| child.kind() == "identifier")
2064}
2065
2066/// The modifier word and inner declarator a `macro_modifier_declarator`
2067/// wraps: the grammar's structured reading of an unexpanded object-like macro
2068/// before a declarator. In a statement-context class-body reparse the modifier
2069/// can also be the member's return type, which the grammar could not separate
2070/// from the declarator and admitted as a modifier instead.
2071pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2072    if node.kind() != "macro_modifier_declarator" {
2073        return None;
2074    }
2075    let mut cursor = node.walk();
2076    let modifier = node
2077        .named_children(&mut cursor)
2078        .find(|child| child.kind() == "macro_modifier")
2079        .and_then(macro_modifier_identifier)?;
2080    let declarator = node.child_by_field_name("declarator")?;
2081    Some((modifier, declarator))
2082}
2083
2084/// Whether `node` can only be part of what precedes a declarator -- a type, a
2085/// specifier, or a word of the attribute macro's own text that the lexer left
2086/// as a bare identifier or, under the macro-declaration grammar, wrapped as a
2087/// `macro_modifier` -- so a run of these followed by a declarator is one
2088/// declaration the parser failed to group.
2089fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2090    matches!(
2091        node.kind(),
2092        "identifier"
2093            | "type_identifier"
2094            | "primitive_type"
2095            | "sized_type_specifier"
2096            | "struct_specifier"
2097            | "union_specifier"
2098            | "enum_specifier"
2099            | "type_qualifier"
2100            | "storage_class_specifier"
2101            | "explicit_function_specifier"
2102            | "virtual_function_specifier"
2103            | "qualified_identifier"
2104            | "template_type"
2105            | "dependent_type"
2106            | "placeholder_type_specifier"
2107            | "macro_modifier"
2108            | "macro_type_specifier"
2109    )
2110}
2111
2112/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
2113/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
2114/// words the lexer reports as bare identifiers. Anything else in such an
2115/// `ERROR` stops the recovery, so a macro argument that carries structure this
2116/// recovery does not understand is never guessed at.
2117fn is_macro_argument_error(node: Node<'_>) -> bool {
2118    if node.kind() != "ERROR" {
2119        return false;
2120    }
2121    let mut cursor = node.walk();
2122    node.named_children(&mut cursor).all(|child| {
2123        matches!(
2124            child.kind(),
2125            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2126        )
2127    })
2128}
2129
2130/// The end of a recovered declaration: the declarator's end, extended across
2131/// the `;` the grammar left beside it inside the envelope.
2132///
2133/// The envelope's own end is the hard boundary. The parser hands the last
2134/// declaration's `;` to the sibling statement it recovered with, outside the
2135/// envelope, and a range that reached it would no longer lie inside one node --
2136/// which is how every reader (including the resolver's climb from a range to
2137/// the node that declares it) finds a recovered declaration again.
2138fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2139    declarator
2140        .next_sibling()
2141        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2142        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2143}
2144
2145/// The declarations `node` holds after an attribute-like macro cost the parser
2146/// their grouping, in source order.
2147///
2148/// The parser packs the parts into whichever slots it has left. In whisper's
2149/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
2150/// field takes the export macro, the real return type and the *next*
2151/// declaration's declarator both end up inside one sibling `ERROR`, and the
2152/// `declarator` field takes whatever declaration the recovery reached last. In
2153/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
2154/// arrive as bare identifiers and the attributed member and the member after it
2155/// share one declarator node.
2156///
2157/// Both are the same failure, so both get the same reading: flatten the node's
2158/// parts -- splicing each nested `ERROR`'s own children in place, since an
2159/// `ERROR` here is only a grouping failure -- and read the flat run as what it
2160/// spells, a run of type-and-specifier tokens followed by a declarator, over
2161/// and over.
2162///
2163/// Fails closed. A part that is neither a declarator nor something that can
2164/// only precede one ends the recovery there, and the declarations before it are
2165/// kept.
2166fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2167    let mut parts = Vec::new();
2168    let mut cursor = node.walk();
2169    for child in node.named_children(&mut cursor) {
2170        if child.kind() == "ERROR" {
2171            let mut error_cursor = child.walk();
2172            parts.extend(child.named_children(&mut error_cursor));
2173        } else {
2174            parts.push(child);
2175        }
2176    }
2177
2178    let mut declarations = Vec::new();
2179    let mut start = None;
2180    let mut is_static = false;
2181    let mut complete = true;
2182    for part in parts {
2183        if part.kind() == "comment" {
2184            continue;
2185        }
2186        if let Some(declarator) = extract_function_declarator(part) {
2187            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2188            declarations.push(MacroWrappedDeclaration {
2189                declarator,
2190                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2191                is_static,
2192            });
2193            is_static = false;
2194            continue;
2195        }
2196        if !is_recovered_declaration_type_part(part) {
2197            complete = false;
2198            break;
2199        }
2200        is_static |= part.kind() == "storage_class_specifier"
2201            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2202        start.get_or_insert(part.start_byte());
2203    }
2204    StrandedRun {
2205        declarations,
2206        complete: complete && start.is_none(),
2207    }
2208}
2209
2210/// The declarations an attribute-like macro invocation swallowed into a
2211/// declaration-scope `ERROR`, in source order.
2212///
2213/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2214/// declaration in a macro call:
2215///
2216/// ```text
2217/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2218///                  struct llama_model * model,
2219///           struct llama_context_params   params),
2220///         "use llama_init_from_model instead");
2221/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2222/// ```
2223///
2224/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2225/// holding the macro name, the wrapped declaration as a
2226/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2227/// following declaration as a further `parameter_declaration` until it
2228/// recovers. That is what removed `llama_tokenize` from the index and left the
2229/// seven-argument call in `talk-llama.cpp` with only that file's own
2230/// three-parameter `static` overload to choose from (#2552, and the
2231/// `LLAMA_API` half of #2551).
2232///
2233/// Every part of every swallowed declaration is still a real node; only their
2234/// grouping is lost. This rebuilds the grouping and reads the nodes.
2235///
2236/// Under the macro-declaration grammar the same invocation keeps the same
2237/// `ERROR` envelope, but the parts inside it are structured further: the macro
2238/// head is a whole `function_declarator` whose parameter list holds the
2239/// arguments, an export-macro word is a `macro_modifier`, a following
2240/// declaration is a `macro_modifier_declarator` or a bare
2241/// `function_declarator` in a sibling `ERROR`, and the hint string explodes
2242/// into word nodes between the `ERROR` nodes that hold its quotes. The reading
2243/// is the same either way: the invocation's own arguments, then one
2244/// declaration at a time until something that is not a declaration stops it.
2245fn macro_wrapped_declarations<'tree>(
2246    envelope: Node<'tree>,
2247    source: &str,
2248    ancestry: &ParentIndex<'tree>,
2249) -> Vec<MacroWrappedDeclaration<'tree>> {
2250    let mut declarations = Vec::new();
2251    if !is_declaration_scope_error(envelope, ancestry) {
2252        return declarations;
2253    }
2254    let mut cursor = envelope.walk();
2255    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2256    let [head, rest @ ..] = children.as_slice() else {
2257        return declarations;
2258    };
2259    let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2260        rest.to_vec()
2261    } else if head.kind() == "function_declarator"
2262        && head
2263            .child_by_field_name("declarator")
2264            .is_some_and(|name| name.kind() == "identifier")
2265    {
2266        let Some(parameters) = head
2267            .child_by_field_name("parameters")
2268            .filter(|parameters| parameters.kind() == "parameter_list")
2269        else {
2270            return declarations;
2271        };
2272        let mut parameters_cursor = parameters.walk();
2273        parameters.named_children(&mut parameters_cursor).collect()
2274    } else {
2275        return declarations;
2276    };
2277    let mut wrapped_declaration_seen = false;
2278    // The hint string's exploded words sit between the `ERROR` nodes holding
2279    // its quotes; inside that bracket they are string content, not
2280    // declaration parts, and they must not start a declaration's range.
2281    let mut in_hint = false;
2282    // Where the specifier words gathered for the declaration whose declarator
2283    // has not arrived yet. Only the macro-declaration grammar's envelopes use
2284    // this: in them an export-macro word is a sibling of the declarator
2285    // rather than a part of its `parameter_declaration`.
2286    let mut pending_start = None;
2287    let mut is_static = false;
2288    for argument in arguments {
2289        if argument.kind() == "comment" {
2290            continue;
2291        }
2292        if argument.kind() == "ERROR"
2293            && (0..argument.child_count())
2294                .filter(|index| {
2295                    argument
2296                        .child(*index)
2297                        .is_some_and(|child| child.kind() == "\"")
2298                })
2299                .count()
2300                % 2
2301                == 1
2302        {
2303            // One of the quote `ERROR`s bracketing the hint string.
2304            in_hint = !in_hint;
2305            pending_start = None;
2306            continue;
2307        }
2308        if in_hint {
2309            continue;
2310        }
2311        match argument.kind() {
2312            "parameter_declaration" => {
2313                let recovered = stranded_declaration_run(argument, source).declarations;
2314                if recovered.is_empty() {
2315                    break;
2316                }
2317                wrapped_declaration_seen = true;
2318                declarations.extend(recovered);
2319            }
2320            // A specifier word of the declaration whose declarator the parser
2321            // handed to a later sibling.
2322            "macro_modifier" if wrapped_declaration_seen => {
2323                pending_start.get_or_insert(argument.start_byte());
2324            }
2325            "storage_class_specifier" if wrapped_declaration_seen => {
2326                pending_start.get_or_insert(argument.start_byte());
2327                is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2328            }
2329            "macro_modifier_declarator" if wrapped_declaration_seen => {
2330                let Some(declarator) = extract_function_declarator(argument) else {
2331                    break;
2332                };
2333                let start = pending_start
2334                    .take()
2335                    .unwrap_or_else(|| argument.start_byte());
2336                declarations.push(MacroWrappedDeclaration {
2337                    declarator,
2338                    range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2339                    is_static,
2340                });
2341                is_static = false;
2342            }
2343            // The hint string, and only that: an argument the recovery cannot
2344            // read as a declaration is admitted before the wrapped declaration
2345            // is found, so a macro whose first argument is not a declaration
2346            // recovers nothing.
2347            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2348            // A declaration the parser shredded to a bare function declarator
2349            // and its terminator, its specifier words left as the siblings
2350            // before it.
2351            "ERROR" if wrapped_declaration_seen => {
2352                let mut error_cursor = argument.walk();
2353                let mut parts = argument.named_children(&mut error_cursor);
2354                let Some(declarator) = parts
2355                    .next()
2356                    .filter(|part| part.kind() == "function_declarator")
2357                    .filter(|_| parts.next().is_none())
2358                else {
2359                    break;
2360                };
2361                let start = pending_start
2362                    .take()
2363                    .unwrap_or_else(|| declarator.start_byte());
2364                declarations.push(MacroWrappedDeclaration {
2365                    declarator,
2366                    range: cpp_recovery_window(
2367                        source,
2368                        start,
2369                        recovered_declaration_end(declarator),
2370                    ),
2371                    is_static,
2372                });
2373                is_static = false;
2374            }
2375            _ => break,
2376        }
2377    }
2378    declarations
2379}
2380
2381/// What one macro invocation swallowed when it collapsed a whole run of
2382/// declarations.
2383struct CollapsedMacroDeclarationRun {
2384    /// The byte just past the `;` that closes the invocation, which is where
2385    /// the declarations it swallowed begin.
2386    invocation_end: usize,
2387    /// The byte this recovery owns to: the close of the enclosing declaration
2388    /// scope when the invocation swallowed the declarations written after it,
2389    /// and the invocation's own end when the parser instead spread the
2390    /// invocation itself across the container's children and swallowed nothing.
2391    region_end: usize,
2392}
2393
2394/// The tokens one collapsed macro invocation spans, in source order: the leaves
2395/// of `node`, then the leaves of the siblings written after it. Comments and
2396/// the parser's own MISSING tokens are skipped; neither is in the source the
2397/// invocation is written in.
2398///
2399/// The parser puts the parts of a failed invocation wherever it has room. It
2400/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2401/// `expression_statement` it recovered with; it spreads them flat over the
2402/// container's children; it packs the tail of the file into one of the
2403/// arguments. The grouping therefore says nothing, but the token order still
2404/// spells the invocation, which is what this reads.
2405struct MacroInvocationTokens<'tree> {
2406    stack: Vec<Node<'tree>>,
2407    /// The last sibling handed to `stack`, and the point the next one is asked
2408    /// for. Held as the node rather than as its already-computed successor so
2409    /// the successor is asked for only when the walk actually runs off the end
2410    /// of what it has: `Node::next_sibling` recovers the parent first, which
2411    /// re-descends from the root, so it costs the node's position in the tree.
2412    /// Nearly every node this iterator is built for is rejected on its first
2413    /// token, before any sibling is needed, and computing one eagerly made that
2414    /// rejection cost a walk of the whole container (capstone's generated
2415    /// instruction tables, t19).
2416    frontier: Option<Node<'tree>>,
2417}
2418
2419impl<'tree> MacroInvocationTokens<'tree> {
2420    fn new(node: Node<'tree>) -> Self {
2421        Self {
2422            stack: vec![node],
2423            frontier: Some(node),
2424        }
2425    }
2426}
2427
2428impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2429    type Item = Node<'tree>;
2430
2431    fn next(&mut self) -> Option<Node<'tree>> {
2432        loop {
2433            let Some(node) = self.stack.pop() else {
2434                let sibling = self.frontier?.next_sibling()?;
2435                self.frontier = Some(sibling);
2436                self.stack.push(sibling);
2437                continue;
2438            };
2439            if node.child_count() == 0 {
2440                if node.kind() == "comment" || node.is_missing() {
2441                    continue;
2442                }
2443                return Some(node);
2444            }
2445            let mut cursor = node.walk();
2446            let children = node.children(&mut cursor).collect::<Vec<_>>();
2447            self.stack.extend(children.into_iter().rev());
2448        }
2449    }
2450}
2451
2452/// The macro invocation that begins at `node` when the parser could not group
2453/// it, or `None` when `node` does not begin one.
2454///
2455/// whisper.cpp's bundled `llama.h` writes
2456///
2457/// ```text
2458/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2459///                          const char * path_model,
2460///           struct llama_model_params   params),
2461///         "use llama_model_load_from_file instead");
2462/// ```
2463///
2464/// and its own `include/whisper.h` writes
2465///
2466/// ```text
2467/// WHISPER_DEPRECATED(
2468///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2469///     "use whisper_init_from_file_with_params instead"
2470/// );
2471/// ```
2472///
2473/// tree-sitter cannot know either name is a macro, so it reads the name and its
2474/// `(` as a function declarator whose close it never finds, and carries that
2475/// failure forward over the declarations written after it. In `llama.h` that
2476/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2477/// (#2551). In `whisper.h` it instead flattens the invocations over the
2478/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2479/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2480/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2481/// `function_definition` when a brace block falls in a swallowed tail, because
2482/// the parser borrows it for the body the bogus definition needs, and an
2483/// `ERROR` or a bare token when none does. None of that says anything about the
2484/// construct, so the node's kind is not the criterion; the token order is.
2485///
2486/// The invocation ends at the `)` that closes its own `(`, immediately followed
2487/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2488/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2489/// its own, so a `;` reached before the close means this is some other
2490/// construct -- a macro-defined function body, say -- and the ordinary readers
2491/// keep the node.
2492///
2493/// Fails closed everywhere else too. The head must be an export-macro-shaped
2494/// token followed by `(`, the node must stand where declarations live, and an
2495/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2496/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2497/// nothing past it -- is left to that reader.
2498fn collapsed_macro_declaration_run<'tree>(
2499    node: Node<'tree>,
2500    source: &str,
2501    ancestry: &ParentIndex<'tree>,
2502) -> Option<CollapsedMacroDeclarationRun> {
2503    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2504    let mut tokens = MacroInvocationTokens::new(node);
2505    let name = tokens.next()?;
2506    if !matches!(name.kind(), "identifier" | "type_identifier")
2507        || !cpp_export_macro_token(node_text(name, source))
2508    {
2509        return None;
2510    }
2511    if tokens.next()?.kind() != "(" {
2512        return None;
2513    }
2514    let mut depth = 1usize;
2515    let invocation_end = loop {
2516        let token = tokens.next()?;
2517        match token.kind() {
2518            "(" => depth += 1,
2519            ";" => return None,
2520            ")" => {
2521                depth -= 1;
2522                if depth == 0 {
2523                    let semicolon = tokens.next()?;
2524                    if semicolon.kind() != ";" {
2525                        return None;
2526                    }
2527                    break semicolon.end_byte();
2528                }
2529            }
2530            _ => {}
2531        }
2532    };
2533    // An invocation that reaches the end of its own declaration scope swallowed
2534    // nothing: there is nothing after it left to recover, and the ordinary
2535    // readers have the invocation itself. This is also what stops the scan
2536    // below from re-entering itself, since the region it reparses for one
2537    // invocation ends exactly at that invocation's `;`.
2538    if invocation_end >= container_end {
2539        return None;
2540    }
2541    // Where the damage ends. A node that reaches past its own invocation packed
2542    // real declarations into the invocation's argument list, so the parser was
2543    // still failing when it got there and everything to the close of the scope
2544    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2545    // line 455 with it, and every declaration from there to the end of the file
2546    // is shredded. A node that stops at or before the invocation's `;` holds
2547    // nothing but the invocation, whose own parts the parser spread over the
2548    // siblings after it.
2549    let region_end = if node.end_byte() > invocation_end {
2550        container_end
2551    } else {
2552        invocation_end
2553    };
2554    Some(CollapsedMacroDeclarationRun {
2555        invocation_end,
2556        region_end,
2557    })
2558}
2559
2560/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2561/// ordinary class body, and the members it swallowed.
2562///
2563/// Botan deprecates members that way:
2564///
2565/// ```text
2566/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2567/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2568/// ```
2569///
2570/// The parser reads the macro name as the member's type and its argument list
2571/// as a parenthesized declarator, which then swallows the attributed member
2572/// *and* the member written after it: a `field_declaration` whose `type` is a
2573/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2574/// opening with an `ERROR` whose first part is a bare identifier -- the first
2575/// word of the string, which the lexer could not keep together.
2576///
2577/// The macro's spelling is not the criterion; that structural shape is. The
2578/// returned declarations are in source order, the first being the attributed
2579/// member and the rest the members the declarator swallowed after it.
2580fn string_attribute_macro_member_declarators<'tree>(
2581    field: Node<'tree>,
2582    source: &str,
2583) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2584    if field.kind() != "field_declaration"
2585        || field
2586            .child_by_field_name("type")
2587            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2588    {
2589        return None;
2590    }
2591    let declarator = field.child_by_field_name("declarator")?;
2592    if declarator.kind() != "parenthesized_declarator" {
2593        return None;
2594    }
2595    let opening = declarator.named_child(0)?;
2596    if opening.kind() != "ERROR"
2597        || opening
2598            .named_child(0)
2599            .is_none_or(|word| word.kind() != "identifier")
2600    {
2601        return None;
2602    }
2603    let declarations = stranded_declaration_run(declarator, source).declarations;
2604    (!declarations.is_empty()).then_some(declarations)
2605}
2606
2607/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2608/// export-macro class body leaves in front of the members that macro decorated.
2609///
2610/// The reparse reads the class body as statements, so the attribute becomes a
2611/// call statement of its own -- with the `;` the grammar had to invent -- and
2612/// the members after it are stranded in the `ERROR` that follows.
2613fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2614    let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2615        .then(|| node.named_child(0))
2616        .flatten()
2617        .filter(|child| child.kind() == "call_expression")
2618    else {
2619        return false;
2620    };
2621    call.child_by_field_name("function")
2622        .is_some_and(|function| function.kind() == "identifier")
2623        && call
2624            .child_by_field_name("arguments")
2625            .is_some_and(|arguments| {
2626                let mut cursor = arguments.walk();
2627                arguments.named_child_count() > 0
2628                    && arguments
2629                        .named_children(&mut cursor)
2630                        .all(|argument| argument.kind() == "string_literal")
2631            })
2632}
2633
2634/// The start byte of the call the region reparse left where an access-labeled
2635/// constructor was written.
2636///
2637/// A constructor is a member only inside a class body. The export-macro class
2638/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2639/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2640/// comma-expression under `private:`, with no declarator left in the tree. It
2641/// is still spelled in the source, though, starting at the one call under the
2642/// label whose callee is the class's own name. Botan's private six-parameter
2643/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2644/// call is an ambiguity this declines, and so is a reparse from that byte that
2645/// does not yield exactly one constructor declarator there.
2646fn cpp_access_label_constructor_call_start(
2647    node: Node<'_>,
2648    class_name: &str,
2649    source: &str,
2650) -> Option<usize> {
2651    if node.kind() != "labeled_statement" {
2652        return None;
2653    }
2654    let label = node.named_child(0)?;
2655    if label.kind() != "statement_identifier"
2656        || !matches!(
2657            node_text(label, source).trim(),
2658            "public" | "private" | "protected"
2659        )
2660    {
2661        return None;
2662    }
2663    let mut starts = Vec::new();
2664    let mut stack = vec![node];
2665    while let Some(current) = stack.pop() {
2666        if current.kind() == "call_expression"
2667            && current
2668                .child_by_field_name("function")
2669                .is_some_and(|function| {
2670                    function.kind() == "identifier"
2671                        && node_text(function, source).trim() == class_name
2672                })
2673        {
2674            starts.push(current.start_byte());
2675        }
2676        // The macro-declaration grammar reads the constructor head as a
2677        // declaration whose type is the class name and whose declarators are
2678        // the shredded parameter list and initializers; the head's start is
2679        // the type's start.
2680        if current.kind() == "declaration"
2681            && current
2682                .child_by_field_name("type")
2683                .is_some_and(|type_node| {
2684                    type_node.kind() == "type_identifier"
2685                        && node_text(type_node, source).trim() == class_name
2686                })
2687        {
2688            starts.push(
2689                current
2690                    .child_by_field_name("type")
2691                    .expect("checked declaration type")
2692                    .start_byte(),
2693            );
2694        }
2695        let mut cursor = current.walk();
2696        stack.extend(current.named_children(&mut cursor));
2697    }
2698    let [start] = starts.as_slice() else {
2699        return None;
2700    };
2701    Some(*start)
2702}
2703
2704/// The `function_definition` that gives `declarator` a body, following only the
2705/// declarator chain, so a recovered callable knows whether it is a declaration
2706/// or a definition without anyone having to say.
2707fn cpp_declarator_function_definition<'tree>(
2708    declarator: Node<'tree>,
2709    ancestry: &ParentIndex<'tree>,
2710) -> Option<Node<'tree>> {
2711    let mut current = declarator;
2712    while let Some(parent) = ancestry.parent(current) {
2713        match parent.kind() {
2714            "function_definition" if parent.child_by_field_name("body").is_some() => {
2715                return Some(parent);
2716            }
2717            "pointer_declarator"
2718            | "reference_declarator"
2719            | "parenthesized_declarator"
2720            | "array_declarator" => current = parent,
2721            _ => return None,
2722        }
2723    }
2724    None
2725}
2726
2727/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2728/// declaration walk still reaches, so a recovery that runs ahead of that walk
2729/// would name what it finds without the namespace.
2730fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2731    let mut current = node;
2732    while let Some(parent) = ancestry.parent(current) {
2733        if parent.kind() == "namespace_definition"
2734            && parent.child_by_field_name("body") == Some(current)
2735        {
2736            return true;
2737        }
2738        current = parent;
2739    }
2740    false
2741}
2742
2743/// Whether the source a recovered callable's byte range spells is a definition
2744/// (a declarator with a body) rather than a declaration.
2745///
2746/// A callable recovered from a mangled region owns no node of its own in the
2747/// file's tree, so the occurrence-role scan that climbs from the range to the
2748/// node containing it lands on whatever container the parser left and answers
2749/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2750/// constructor a declaration and left its call site with nothing to navigate to
2751/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2752/// extraction used, and read the answer from the one declaration it spells.
2753///
2754/// `None` when the range is not one recovered declaration on its own, which is
2755/// the case for every ordinary declaration, so callers keep their own answer.
2756pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2757    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2758    let root = tree.root_node();
2759    let mut cursor = root.walk();
2760    let items = root
2761        .named_children(&mut cursor)
2762        .filter(|child| child.kind() != "comment")
2763        .collect::<Vec<_>>();
2764    let [item] = items.as_slice() else {
2765        return None;
2766    };
2767    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2768        return None;
2769    }
2770    match item.kind() {
2771        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2772        "declaration" | "field_declaration" => Some(false),
2773        _ => None,
2774    }
2775}
2776
2777/// Whether `node` is an envelope an attribute-like macro invocation left where
2778/// declarations were written: the declaration-scope `ERROR`
2779/// [`macro_wrapped_declarations`] reads, or the collapsed run
2780/// [`collapsed_macro_declaration_run`] reads.
2781///
2782/// Extraction and resolution must read one definition of this shape. The
2783/// resolver climbs from a declaration's recorded byte range to the node that
2784/// declares it, and a recovered declaration's range lies inside one of these
2785/// envelopes rather than inside a `declaration` node, so the climb stops here
2786/// (the same role `is_recovered_exported_class_container` plays for a recovered
2787/// class).
2788pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2789    let ancestry = ParentIndex::unindexed();
2790    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2791        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2792}
2793
2794fn recover_exported_class_function_definition<'tree>(
2795    node: Node<'tree>,
2796    source: &str,
2797) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2798    if node.kind() != "function_definition" {
2799        return None;
2800    }
2801    if let Some(prefix) = node.prev_named_sibling()
2802        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2803        && recovered.range.end_byte == node.end_byte()
2804    {
2805        return Some((
2806            node,
2807            recovered.name,
2808            recovered.raw_supertypes,
2809            recovered.base_facts,
2810        ));
2811    }
2812    let type_node = node.child_by_field_name("type")?;
2813    let declarator = node.child_by_field_name("declarator")?;
2814
2815    if matches!(
2816        type_node.kind(),
2817        "class_specifier" | "struct_specifier" | "union_specifier"
2818    ) {
2819        let type_name = type_node
2820            .child_by_field_name("name")
2821            .and_then(|name| direct_identifier_name(name, source));
2822        let exported_macro_type = type_name
2823            .as_ref()
2824            .is_some_and(|name| cpp_export_macro_token(name));
2825        if exported_macro_type {
2826            let mut cursor = node.walk();
2827            let errors_before_declarator = node
2828                .named_children(&mut cursor)
2829                .filter(|child| {
2830                    child.kind() == "ERROR"
2831                        && child.start_byte() >= type_node.end_byte()
2832                        && child.end_byte() <= declarator.start_byte()
2833                })
2834                .collect::<Vec<_>>();
2835            if let Some(name) = errors_before_declarator
2836                .iter()
2837                .find_map(|error| displaced_exported_class_name(*error, source))
2838            {
2839                let base_node = errors_before_declarator
2840                    .iter()
2841                    .any(|error| malformed_inheritance_syntax(*error))
2842                    .then_some(declarator);
2843                let raw_supertypes = base_node
2844                    .and_then(|base| recovered_malformed_base_name(base, source))
2845                    .map(|base| vec![base]);
2846                let base_facts = base_node
2847                    .and_then(|base| {
2848                        recovered_base_specifier(
2849                            base,
2850                            source,
2851                            recovered_base_is_virtual(node, base, source),
2852                        )
2853                    })
2854                    .map(|base| vec![base]);
2855                return Some((node, name, raw_supertypes, base_facts));
2856            }
2857            if errors_before_declarator
2858                .iter()
2859                .any(|error| malformed_inheritance_syntax(*error))
2860            {
2861                return None;
2862            }
2863        }
2864        if !exported_macro_type
2865            && let Some(name) = type_name
2866            && !cpp_export_macro_token(&name)
2867            && let Some(base) =
2868                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2869        {
2870            let base_node =
2871                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2872            let base_facts = Some(vec![recovered_base_specifier(
2873                base_node,
2874                source,
2875                recovered_base_is_virtual(node, base_node, source),
2876            )?]);
2877            return Some((node, name, Some(vec![base]), base_facts));
2878        }
2879        // The macro-declaration grammar reads the displaced class name and the
2880        // `final` that follows it as a `macro_modifier_declarator`; read the
2881        // name positionally from the head's tokens, the same rule the other
2882        // recovered export heads use (#2557).
2883        let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2884            (declarator.kind() == "macro_modifier_declarator")
2885                .then(|| node.child_by_field_name("body"))
2886                .flatten()
2887                .filter(|body| body.kind() == "compound_statement")
2888                .and_then(|body| recovered_export_head_name(node, body, source))
2889                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2890        });
2891        if let Some(name) = declarator_name
2892            && exported_macro_type
2893            && !cpp_export_macro_token(&name)
2894        {
2895            let base_node = exported_macro_type
2896                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2897                .flatten();
2898            let raw_supertypes = base_node
2899                .and_then(|base| recovered_malformed_base_name(base, source))
2900                .map(|base| vec![base]);
2901            let base_facts = base_node
2902                .and_then(|base| {
2903                    recovered_base_specifier(
2904                        base,
2905                        source,
2906                        recovered_base_is_virtual(node, base, source),
2907                    )
2908                })
2909                .map(|base| vec![base]);
2910            return Some((node, name, raw_supertypes, base_facts));
2911        }
2912        if declarator.kind() == "parenthesized_declarator"
2913            && type_node
2914                .child_by_field_name("name")
2915                .and_then(|name| direct_identifier_name(name, source))
2916                .is_some_and(|name| cpp_export_macro_token(&name))
2917        {
2918            if let Some((name, base_node)) =
2919                recovered_function_like_export_class_owner_node(declarator, source)
2920            {
2921                return Some((
2922                    node,
2923                    name,
2924                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2925                    Some(vec![recovered_base_specifier(
2926                        base_node,
2927                        source,
2928                        recovered_base_is_virtual(node, base_node, source),
2929                    )?]),
2930                ));
2931            }
2932            let body_start = node
2933                .child_by_field_name("body")
2934                .map(|body| body.start_byte())
2935                .unwrap_or(node.end_byte());
2936            let mut cursor = node.walk();
2937            if let Some(name) = node
2938                .named_children(&mut cursor)
2939                .filter(|child| {
2940                    child.kind() == "ERROR"
2941                        && child.start_byte() >= declarator.end_byte()
2942                        && child.end_byte() <= body_start
2943                })
2944                .find_map(|error| declarator_name_from_node(error, source))
2945            {
2946                return Some((node, name, None, None));
2947            }
2948            // The macro-declaration grammar keeps a head without a base clause
2949            // whole inside the declarator: `( ERROR(2, 2)) TOTP final` with a
2950            // missing `)`, the name and `final` wrapped as a
2951            // `macro_modifier_declarator` or the name left bare. Read the name
2952            // positionally from the declarator's own tokens (#2557). The macro
2953            // is the `type` field, so a capitalized name (`TOTP`) is the class.
2954            if let Some(body) = node
2955                .child_by_field_name("body")
2956                .filter(|body| body.kind() == "compound_statement")
2957                && let Some(name_node) =
2958                    export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2959            {
2960                let name = normalize_cpp_whitespace(node_text(name_node, source));
2961                if !name.is_empty() {
2962                    let (bases, base_facts) = recovered_export_head_base_data(
2963                        declarator,
2964                        name_node.end_byte(),
2965                        body.start_byte(),
2966                        source,
2967                    );
2968                    return Some((
2969                        node,
2970                        name,
2971                        (!bases.is_empty()).then_some(bases),
2972                        (!base_facts.is_empty()).then_some(base_facts),
2973                    ));
2974                }
2975            }
2976        }
2977    }
2978
2979    // The macro-declaration grammar reads a namespace-opening macro before an
2980    // export-macro class head (`NS_ENTER(ns) class API Service { ... }`) with
2981    // the invocation as the `macro_type_specifier` type and the class head as
2982    // a `macro_modifier_declarator` chain. The class name is the last head
2983    // token before the body, per the positional rule (#2557).
2984    if type_node.kind() == "macro_type_specifier"
2985        && declarator.kind() == "macro_modifier_declarator"
2986        && type_node
2987            .child_by_field_name("name")
2988            .and_then(|name| direct_identifier_name(name, source))
2989            .is_some_and(|name| cpp_export_macro_token(&name))
2990    {
2991        let body = node
2992            .child_by_field_name("body")
2993            .filter(|body| body.kind() == "compound_statement")?;
2994        let tokens = export_head_tokens(node, Some(body));
2995        let has_class_keyword = tokens.iter().any(|token| {
2996            matches!(token.kind(), "identifier" | "type_identifier")
2997                && matches!(node_text(*token, source), "class" | "struct" | "union")
2998        });
2999        if !has_class_keyword {
3000            return None;
3001        }
3002        let name_node = recovered_export_head_name(node, body, source)?;
3003        let name = normalize_cpp_whitespace(node_text(name_node, source));
3004        if name.is_empty() || cpp_export_macro_token(&name) {
3005            return None;
3006        }
3007        return Some((node, name, None, None));
3008    }
3009
3010    let declarator_text = direct_identifier_name(declarator, source)?;
3011    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
3012        return None;
3013    }
3014    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
3015}
3016
3017fn recovered_function_like_export_class_owner_node<'tree>(
3018    declarator: Node<'tree>,
3019    source: &str,
3020) -> Option<(String, Node<'tree>)> {
3021    if declarator.kind() != "parenthesized_declarator" {
3022        return None;
3023    }
3024    let mut cursor = declarator.walk();
3025    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
3026    let [prefix, base] = children.as_slice() else {
3027        return None;
3028    };
3029    if prefix.kind() != "ERROR"
3030        || !matches!(
3031            base.kind(),
3032            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3033        )
3034    {
3035        return None;
3036    }
3037    let mut identifiers = Vec::new();
3038    let mut prefix_cursor = prefix.walk();
3039    let mut parts = prefix
3040        .named_children(&mut prefix_cursor)
3041        .collect::<Vec<_>>();
3042    // The macro-declaration grammar wraps the displaced name and its `final`
3043    // as a `macro_modifier_declarator`; read the identifiers it wraps the same
3044    // way as the bare ones the older grammar left here.
3045    while let Some(child) = parts.pop() {
3046        match child.kind() {
3047            "number_literal" | "string_literal" | "char_literal" => {}
3048            "identifier" | "type_identifier" => {
3049                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3050            }
3051            "macro_modifier" => {
3052                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3053            }
3054            "macro_modifier_declarator" => {
3055                parts.extend(child.named_children(&mut child.walk()));
3056            }
3057            _ => return None,
3058        }
3059    }
3060    identifiers.reverse();
3061    let name = match identifiers.as_slice() {
3062        [name] => name.clone(),
3063        [name, final_token] if final_token == "final" => name.clone(),
3064        _ => return None,
3065    };
3066    if name.is_empty() || cpp_export_macro_token(&name) {
3067        return None;
3068    }
3069    Some((name, *base))
3070}
3071
3072/// Collect the base names and source facts tree-sitter scattered across the
3073/// recovered head of a function-like export-macro class. A base arrives either
3074/// as a direct sibling identifier or inside the `ERROR` node the grammar
3075/// produced for a `: public Base` fragment. The grammar leaves the `final`
3076/// specifier and access specifiers in the same position as the bases, so drop
3077/// them.
3078/// The bases a recovered function-like export-macro class head names between
3079/// `after` (the end of the class name, or of the access specifier that stands
3080/// in for it) and `before` (the start of the body, or of the `init_declarator`
3081/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
3082/// fragments, and one fragment can also hold the class name and `final`
3083/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
3084/// than by the fragment that holds it.
3085fn recovered_export_head_base_data(
3086    node: Node<'_>,
3087    after: usize,
3088    before: usize,
3089    source: &str,
3090) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3091    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3092    let mut base_nodes = Vec::new();
3093    let mut cursor = node.walk();
3094    for child in node.named_children(&mut cursor) {
3095        if child.kind() == "ERROR" {
3096            let mut error_cursor = child.walk();
3097            base_nodes.extend(
3098                child
3099                    .named_children(&mut error_cursor)
3100                    .filter(within)
3101                    .filter_map(|part| {
3102                        recovered_malformed_base_name(part, source).map(|_| (part, child))
3103                    }),
3104            );
3105        } else if child.kind() == "macro_modifier_declarator" {
3106            // The modifier words are access specifiers or decoration; the base
3107            // is the wrapper's terminal declarator.
3108            let mut current = child;
3109            while let Some(inner) = current.child_by_field_name("declarator") {
3110                if inner.kind() == "macro_modifier_declarator" {
3111                    current = inner;
3112                    continue;
3113                }
3114                if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3115                    base_nodes.push((inner, child));
3116                }
3117                break;
3118            }
3119        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3120            base_nodes.push((child, node));
3121        }
3122    }
3123    let mut bases = Vec::new();
3124    let mut facts = Vec::new();
3125    for (base, container) in base_nodes {
3126        let Some(name) = recovered_malformed_base_name(base, source) else {
3127            continue;
3128        };
3129        if matches!(
3130            name.as_str(),
3131            "final" | "public" | "protected" | "private" | "virtual"
3132        ) {
3133            continue;
3134        }
3135        bases.push(name);
3136        if let Some(fact) = recovered_base_specifier(
3137            base,
3138            source,
3139            recovered_base_is_virtual(container, base, source),
3140        ) {
3141            facts.push(fact);
3142        }
3143    }
3144    (bases, facts)
3145}
3146
3147fn recovered_export_head_bases(
3148    node: Node<'_>,
3149    after: usize,
3150    before: usize,
3151    source: &str,
3152) -> Vec<String> {
3153    recovered_export_head_base_data(node, after, before, source).0
3154}
3155
3156/// Whether `token` is the `final` that closes a recovered class head. The
3157/// grammar keeps it as the `final` keyword when it recognised the head
3158/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
3159/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
3160fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3161    if token.is_named() {
3162        token.kind() == "identifier" && node_text(token, source) == "final"
3163    } else {
3164        token.kind() == "final"
3165    }
3166}
3167
3168/// The class name of a recovered function-like export-macro class head, read by
3169/// position rather than spelling (#2557). `node` is the sibling tree-sitter
3170/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
3171/// carries the body. The head's tokens, in source order with `ERROR` fragments
3172/// flattened, read `macros... name [final] [: bases...]`: the name is the last
3173/// identifier before the head ends, and the head ends at `final`, at the base
3174/// clause `:`, or at `tail`. Every identifier before the name is decoration
3175/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
3176/// (`X509_CA`) is a class name like any other.
3177///
3178/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
3179/// rule, not a token of the source, so it is never the name.
3180fn recovered_export_head_name<'tree>(
3181    node: Node<'tree>,
3182    tail: Node<'tree>,
3183    source: &str,
3184) -> Option<Node<'tree>> {
3185    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3186}
3187
3188/// The class name of a head whose tokens straddle the recovered pair: the
3189/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
3190/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
3191/// The positional rule is the same, read over the two token runs in source
3192/// order, so a name in either node wins by position rather than by which node
3193/// holds it.
3194fn recovered_export_pair_head_name<'tree>(
3195    prefix: Node<'tree>,
3196    sibling: Node<'tree>,
3197    tail: Node<'tree>,
3198    source: &str,
3199) -> Option<Node<'tree>> {
3200    let mut tokens = export_head_tokens(prefix, None);
3201    tokens.extend(export_head_tokens(sibling, Some(tail)));
3202    export_head_name_from_tokens(&tokens, source)
3203}
3204
3205/// One node's head tokens in source order, with `ERROR` fragments spliced in
3206/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
3207/// before `tail` when the caller names one. A `macro_modifier` or
3208/// `macro_modifier_declarator` is the macro-declaration grammar's structured
3209/// form of the same grouping failure, so its children join the run in place
3210/// the same way, recursively.
3211fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3212    let mut tokens = Vec::new();
3213    let mut stack = Vec::new();
3214    let mut cursor = node.walk();
3215    stack.extend(
3216        node.children(&mut cursor)
3217            .collect::<Vec<_>>()
3218            .into_iter()
3219            .rev(),
3220    );
3221    while let Some(child) = stack.pop() {
3222        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3223            break;
3224        }
3225        if matches!(
3226            child.kind(),
3227            "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3228        ) {
3229            let mut fragment_cursor = child.walk();
3230            stack.extend(
3231                child
3232                    .children(&mut fragment_cursor)
3233                    .collect::<Vec<_>>()
3234                    .into_iter()
3235                    .rev(),
3236            );
3237        } else {
3238            tokens.push(child);
3239        }
3240    }
3241    tokens
3242}
3243
3244/// The last identifier before the head ends, per the positional rule.
3245fn export_head_name_from_tokens<'tree>(
3246    tokens: &[Node<'tree>],
3247    source: &str,
3248) -> Option<Node<'tree>> {
3249    let end = tokens
3250        .iter()
3251        .position(|token| {
3252            recovered_export_head_final(*token, source)
3253                || (!token.is_named() && token.kind() == ":")
3254        })
3255        .unwrap_or(tokens.len());
3256    export_head_last_identifier(tokens[..end].iter().copied())
3257}
3258
3259fn export_head_last_identifier<'tree>(
3260    candidates: impl IntoIterator<Item = Node<'tree>>,
3261) -> Option<Node<'tree>> {
3262    candidates
3263        .into_iter()
3264        .filter(|candidate| {
3265            candidate.is_named()
3266                && !candidate.is_missing()
3267                && matches!(
3268                    candidate.kind(),
3269                    "identifier" | "type_identifier" | "field_identifier"
3270                )
3271        })
3272        .last()
3273}
3274
3275/// The `init_declarator` whose `value` carries the class body when the grammar
3276/// keeps a function-like export-macro class head as one `declaration`.
3277fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3278    let mut cursor = declaration.walk();
3279    declaration
3280        .named_children(&mut cursor)
3281        .find(|child| child.kind() == "init_declarator")
3282}
3283
3284/// Read the bases and the initializer-list body of a `declaration`-shaped tail
3285/// of a function-like export-macro class head. The grammar splits a base list
3286/// across bare declarator fields, `ERROR` fragments, and one trailing
3287/// `init_declarator` whose `value` is the class body. `head_end` is where the
3288/// class identity ends and the bases can begin: the end of the access specifier
3289/// when the class name went to a statement label, and the end of the class name
3290/// itself otherwise.
3291fn recovered_export_declaration_tail<'tree>(
3292    declaration: Node<'tree>,
3293    head_end: usize,
3294    source: &str,
3295) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3296    let init = recovered_export_init_declarator(declaration)?;
3297    let body = init.child_by_field_name("value")?;
3298    if body.kind() != "initializer_list" {
3299        return None;
3300    }
3301    let (mut bases, mut facts) =
3302        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3303    let (more_bases, more_facts) =
3304        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3305    bases.extend(more_bases);
3306    facts.extend(more_facts);
3307    Some((bases, (!facts.is_empty()).then_some(facts), body))
3308}
3309
3310/// Whether `node` is the head fragment tree-sitter leaves where
3311/// `class MACRO(args)` was written: the class keyword and the export macro's
3312/// function-like invocation in that order, with nothing else between them.
3313/// Spelling plays no part -- the invocation's `(` following the macro
3314/// identifier directly is what makes it function-like (#2557).
3315///
3316/// Two reductions produce the head. On its own the grammar keeps a body-less
3317/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
3318/// with the invocation's `(`, arguments and `)` beside it. After object-like
3319/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
3320/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
3321/// `declaration` whose class keyword is demoted to a bare `identifier` and
3322/// whose macro invocation becomes the trailing `init_declarator` (#2924).
3323fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3324    match node.kind() {
3325        "ERROR" => {
3326            let Some(class_node) = first_class_like_child(node) else {
3327                return false;
3328            };
3329            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3330                return false;
3331            }
3332            if class_node
3333                .child_by_field_name("name")
3334                .and_then(|name| direct_identifier_name(name, source))
3335                .is_none()
3336            {
3337                return false;
3338            }
3339            class_node
3340                .next_sibling()
3341                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3342        }
3343        "declaration" => {
3344            let mut cursor = node.walk();
3345            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3346            // The class keyword lost its own node: it survives as a bare
3347            // identifier spelled `class`, `struct` or `union`, which is never a
3348            // declarator or a type in well-formed code. It can sit inside an
3349            // `ERROR` beside the object-like macro names, and an `ERROR` here is
3350            // a grouping failure rather than a construct, so read through it.
3351            let Some(keyword) = children
3352                .iter()
3353                .copied()
3354                .flat_map(|child| {
3355                    let mut cursor = child.walk();
3356                    if child.kind() == "ERROR" {
3357                        child.named_children(&mut cursor).collect::<Vec<_>>()
3358                    } else {
3359                        vec![child]
3360                    }
3361                })
3362                .find(|child| {
3363                    child.kind() == "identifier"
3364                        && matches!(node_text(*child, source), "class" | "struct" | "union")
3365                })
3366            else {
3367                return false;
3368            };
3369            children.last().is_some_and(|init| {
3370                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3371                    let mut cursor = init.walk();
3372                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3373                    matches!(parts.as_slice(), [macro_name, arguments]
3374                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
3375                            && arguments.kind() == "argument_list")
3376                }
3377            })
3378        }
3379        _ => false,
3380    }
3381}
3382
3383fn recover_function_like_export_class_pair(
3384    node: Node<'_>,
3385    source: &str,
3386) -> Option<RecoveredFunctionLikeExportClassPair> {
3387    if !is_function_like_export_class_head(node, source) {
3388        return None;
3389    }
3390    let sibling = node.next_named_sibling()?;
3391    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3392        // At translation-unit scope tree-sitter can leave the malformed class
3393        // head in one ERROR node and parse its body as the adjacent compound
3394        // statement. The head still carries the export invocation, displaced
3395        // class name, and optional `final` token in source order, so recover
3396        // the name by the same positional rule as the other shapes.
3397        "compound_statement" => (
3398            recovered_export_pair_head_name(node, sibling, sibling, source)
3399                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3400            None,
3401            None,
3402            sibling,
3403        ),
3404        "expression_statement" => {
3405            let compound = sibling.named_child(0)?;
3406            if compound.kind() != "compound_literal_expression" {
3407                return None;
3408            }
3409            let body = compound.child_by_field_name("value")?;
3410            if body.kind() != "initializer_list" {
3411                return None;
3412            }
3413            (
3414                compound
3415                    .child_by_field_name("type")
3416                    .and_then(|name| direct_identifier_name(name, source))?,
3417                None,
3418                None,
3419                body,
3420            )
3421        }
3422        "labeled_statement" => {
3423            let label = sibling.child_by_field_name("label")?;
3424            if label.kind() != "statement_identifier" {
3425                return None;
3426            }
3427            let name = normalize_cpp_whitespace(node_text(label, source));
3428            let declaration = sibling
3429                .named_children(&mut sibling.walk())
3430                .find(|child| child.kind() == "declaration")?;
3431            let access = declaration.child_by_field_name("type")?;
3432            if !matches!(
3433                node_text(access, source),
3434                "public" | "protected" | "private"
3435            ) {
3436                return None;
3437            }
3438            let (bases, base_facts, body) =
3439                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3440            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3441        }
3442        // `class MACRO(2, 0) Name final { ... };`,
3443        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3444        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3445        // spread over the `type` field, the declarator field, and `ERROR`
3446        // fragments beside them; the name is the last one before the head ends.
3447        "function_definition" => {
3448            let body = sibling.child_by_field_name("body")?;
3449            if body.kind() != "compound_statement" {
3450                return None;
3451            }
3452            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3453            let (bases, base_facts) = recovered_export_head_base_data(
3454                sibling,
3455                name_node.end_byte(),
3456                body.start_byte(),
3457                source,
3458            );
3459            (
3460                normalize_cpp_whitespace(node_text(name_node, source)),
3461                (!bases.is_empty()).then_some(bases),
3462                (!base_facts.is_empty()).then_some(base_facts),
3463                body,
3464            )
3465        }
3466        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3467        // comma-separated base list makes the grammar keep the whole tail as one
3468        // declaration whose trailing `init_declarator` carries the body.
3469        "declaration" => {
3470            let init = recovered_export_init_declarator(sibling)?;
3471            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3472            let (bases, base_facts, body) =
3473                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3474            (
3475                normalize_cpp_whitespace(node_text(name_node, source)),
3476                (!bases.is_empty()).then_some(bases),
3477                base_facts,
3478                body,
3479            )
3480        }
3481        _ => return None,
3482    };
3483    debug_assert!(
3484        !name.is_empty(),
3485        "a recovered class head names its class by an identifier token"
3486    );
3487    let range = Range {
3488        start_byte: node.start_byte(),
3489        end_byte: sibling.end_byte(),
3490        start_line: node.start_position().row + 1,
3491        end_line: sibling.end_position().row + 1,
3492    };
3493    Some(RecoveredFunctionLikeExportClassPair {
3494        name,
3495        raw_supertypes,
3496        base_facts,
3497        range,
3498        fragmented_body: recovered_fragmented_export_body(body, range)?,
3499    })
3500}
3501
3502/// Recover a function-like export-macro class that tree-sitter embedded in a
3503/// larger error after an earlier malformed class body. The grammar still
3504/// preserves every part of the class head: the `class` token, export macro
3505/// identifier and argument list, displaced class identifier, access specifier,
3506/// base field, and initializer-list-shaped body. Match only that complete
3507/// structured sequence and keep each recovered class's exact byte envelope.
3508fn recover_embedded_function_like_export_classes(
3509    node: Node<'_>,
3510    source: &str,
3511) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3512    if !node.is_error() {
3513        return Vec::new();
3514    }
3515
3516    let mut nodes = Vec::new();
3517    let mut class_tokens = Vec::new();
3518    let mut stack = vec![node];
3519    while let Some(current) = stack.pop() {
3520        nodes.push(current);
3521        // An error subtree without an anonymous class token cannot contain
3522        // a recoverable class head. Detect that before sorting or resolving
3523        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3524        if !current.is_named() && current.kind() == "class" {
3525            class_tokens.push(current);
3526        }
3527        push_children_reversed(current, &mut stack);
3528    }
3529    if class_tokens.is_empty() {
3530        return Vec::new();
3531    }
3532    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3533    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3534
3535    // The scans below ask the same handful of kind questions once per node of
3536    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3537    let language = node.language();
3538    let class_kind = NodeKindIds::new(&language, "class");
3539    let identifier_kinds = [
3540        NodeKindIds::new(&language, "identifier"),
3541        NodeKindIds::new(&language, "type_identifier"),
3542        NodeKindIds::new(&language, "field_identifier"),
3543    ];
3544    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3545    let colon_kind = NodeKindIds::new(&language, ":");
3546    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3547
3548    let mut recovered = Vec::new();
3549    for class_token in class_tokens {
3550        debug_assert!(class_kind.matches(class_token));
3551        let row = class_token.start_position().row;
3552        // The head after the `class` token reads `MACRO(args) macros... name
3553        // [final] : bases`. The macro is the first identifier, a function-like
3554        // invocation when its argument list is the next thing after it, and the
3555        // name is the last identifier before the head ends at `final` or at the
3556        // base clause `:`. Spelling plays no part (#2557).
3557        let is_identifier = |candidate: &Node<'_>| {
3558            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3559        };
3560        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3561            candidate.start_byte() >= class_token.end_byte()
3562                && candidate.start_position().row == row
3563                && is_identifier(candidate)
3564        }) else {
3565            continue;
3566        };
3567        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3568            argument_list_kind.matches(*candidate)
3569                && candidate.start_byte() >= macro_name.end_byte()
3570                && candidate.start_position().row == row
3571        }) else {
3572            continue;
3573        };
3574        if nodes.iter().any(|candidate| {
3575            is_identifier(candidate)
3576                && candidate.start_byte() >= macro_name.end_byte()
3577                && candidate.end_byte() <= arguments.start_byte()
3578        }) {
3579            continue;
3580        }
3581        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3582            candidate.start_byte() >= arguments.end_byte()
3583                && (recovered_export_head_final(*candidate, source)
3584                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3585        }) else {
3586            continue;
3587        };
3588        let Some(name_node) =
3589            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3590                is_identifier(candidate)
3591                    && candidate.start_byte() >= arguments.end_byte()
3592                    && candidate.end_byte() <= head_end.start_byte()
3593                    && candidate.start_position().row == row
3594            }))
3595        else {
3596            continue;
3597        };
3598        let name = normalize_cpp_whitespace(node_text(name_node, source));
3599        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3600            field_initializer_kind.matches(*candidate)
3601                && candidate.start_byte() >= name_node.end_byte()
3602                && candidate
3603                    .child_by_field_name("field")
3604                    .or_else(|| candidate.named_child(0))
3605                    .is_some()
3606                && candidate
3607                    .child_by_field_name("value")
3608                    .or_else(|| {
3609                        let mut cursor = candidate.walk();
3610                        candidate
3611                            .named_children(&mut cursor)
3612                            .find(|child| child.kind() == "initializer_list")
3613                    })
3614                    .is_some_and(|value| value.kind() == "initializer_list")
3615        }) else {
3616            continue;
3617        };
3618        let has_access = nodes.iter().copied().any(|candidate| {
3619            candidate.start_byte() >= name_node.end_byte()
3620                && candidate.end_byte() <= base_initializer.start_byte()
3621                && matches!(
3622                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3623                    "public" | "protected" | "private"
3624                )
3625        });
3626        if !has_access {
3627            continue;
3628        }
3629        let Some(base_node) = base_initializer
3630            .child_by_field_name("field")
3631            .or_else(|| base_initializer.named_child(0))
3632        else {
3633            continue;
3634        };
3635        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3636            continue;
3637        };
3638        let body = base_initializer
3639            .child_by_field_name("value")
3640            .or_else(|| {
3641                let mut cursor = base_initializer.walk();
3642                base_initializer
3643                    .named_children(&mut cursor)
3644                    .find(|child| child.kind() == "initializer_list")
3645            })
3646            .expect("initializer-list value checked above");
3647        let range = Range {
3648            start_byte: class_token.start_byte(),
3649            end_byte: body.end_byte(),
3650            start_line: class_token.start_position().row + 1,
3651            end_line: body.end_position().row + 1,
3652        };
3653        if recovered
3654            .iter()
3655            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3656                existing.name == name && existing.range == range
3657            })
3658        {
3659            continue;
3660        }
3661        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3662            continue;
3663        };
3664        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3665            name,
3666            range,
3667            raw_supertypes: vec![base],
3668            base_facts: vec![base_fact],
3669            fragmented_body: match recovered_fragmented_export_body(body, range) {
3670                Some(fragmented) => fragmented,
3671                None => continue,
3672            },
3673        });
3674    }
3675    recovered
3676}
3677
3678fn lifted_function_like_export_class_namespace<'tree>(
3679    node: Node<'tree>,
3680    source: &str,
3681    ancestry: &ParentIndex<'tree>,
3682) -> Option<String> {
3683    // A long malformed body can embed the next exported class several levels
3684    // below a bogus top-level function_definition. Compare namespace evidence
3685    // against that top-level envelope, not only the recovered ERROR's direct
3686    // parent. The source tree still proves the same boundary: one earlier
3687    // malformed namespace and one later standalone closing brace.
3688    let mut anchor = node;
3689    let parent = loop {
3690        let parent = ancestry.parent(anchor)?;
3691        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3692            break parent;
3693        }
3694        anchor = parent;
3695    };
3696    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3697        sibling.start_byte() > anchor.end_byte()
3698            && sibling.kind() == "ERROR"
3699            && sibling.named_child_count() == 0
3700            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3701    });
3702    if !has_later_close {
3703        return None;
3704    }
3705    let candidates = parent
3706        .named_children(&mut parent.walk())
3707        .filter(|sibling| {
3708            sibling.kind() == "namespace_definition"
3709                && sibling.has_error()
3710                && sibling.end_byte() < anchor.start_byte()
3711        })
3712        .filter_map(|namespace| {
3713            namespace
3714                .child_by_field_name("name")
3715                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3716                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3717        })
3718        .collect::<Vec<_>>();
3719    let [namespace] = candidates.as_slice() else {
3720        return None;
3721    };
3722    Some(namespace.clone())
3723}
3724
3725pub(crate) fn recovered_function_like_export_class_pair_has_body(
3726    node: Node<'_>,
3727    source: &str,
3728    identifier: &str,
3729    range: &Range,
3730) -> bool {
3731    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3732        recovered.name == identifier
3733            && recovered.range.start_byte == range.start_byte
3734            && recovered.range.end_byte == range.end_byte
3735    })
3736}
3737
3738fn recovered_exported_class_has_body(
3739    node: Node<'_>,
3740    source: &str,
3741    expected_name: &str,
3742) -> Option<bool> {
3743    match node.kind() {
3744        "function_definition" => {
3745            let (class_node, name, _, _) =
3746                recover_exported_class_function_definition(node, source)?;
3747            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3748        }
3749        "declaration" | "field_declaration" => {
3750            let recovered = recover_exported_class_declaration(node, source)?;
3751            (recovered.name == expected_name).then(|| recovered.body.is_some())
3752        }
3753        _ => None,
3754    }
3755}
3756
3757/// One file's embedded export-macro class recovery, resolved once and keyed by
3758/// the `ERROR` node that mints each set.
3759///
3760/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3761/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3762/// once per class-like unit in the file. On a translation unit the parser could
3763/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3764/// node spans most of the file -- that is one full subtree pass per class,
3765/// quadratic in the file's size, and it was 78% of that file's inverse scan
3766/// (#1496).
3767///
3768/// Keyed by byte span rather than node identity, so one analyzer generation's
3769/// re-parses of the same content share the index. A nested `ERROR` that shares
3770/// its parent's span recovers the same classes: the only node the parent adds
3771/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3772#[derive(Default)]
3773pub struct CppRecoveredExportClassIndex {
3774    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3775}
3776
3777impl CppRecoveredExportClassIndex {
3778    pub fn build(root: Node<'_>, source: &str) -> Self {
3779        let mut by_error_node: HashMap<
3780            (usize, usize),
3781            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3782        > = HashMap::default();
3783        // One cursor for the whole-file walk, and the error question asked by
3784        // symbol rather than by `kind()` string (#3097).
3785        let mut cursor = root.walk();
3786        let mut stack = vec![root];
3787        while let Some(node) = stack.pop() {
3788            if node.is_error() {
3789                let recovered = recover_embedded_function_like_export_classes(node, source);
3790                if !recovered.is_empty() {
3791                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3792                }
3793            }
3794            stack.extend(node.named_children(&mut cursor));
3795        }
3796        Self { by_error_node }
3797    }
3798
3799    /// The bytes this index holds, for the analyzer cache's weight.
3800    pub fn approximate_size(&self) -> usize {
3801        self.by_error_node
3802            .values()
3803            .fold(0usize, |total, recovered| {
3804                recovered.iter().fold(
3805                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3806                    |acc, class| {
3807                        acc.saturating_add(std::mem::size_of::<
3808                            RecoveredEmbeddedFunctionLikeExportClass,
3809                        >())
3810                        .saturating_add(class.name.len())
3811                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3812                    },
3813                )
3814            })
3815    }
3816
3817    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3818        self.by_error_node
3819            .get(&(node.start_byte(), node.end_byte()))
3820            .is_some_and(|recovered| {
3821                recovered.iter().any(|class| {
3822                    class.name == identifier
3823                        && class.range.start_byte == range.start_byte
3824                        && class.range.end_byte == range.end_byte
3825                })
3826            })
3827    }
3828}
3829
3830// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3831// `recovered_class_body_at` pops while deciding whether a recovered class shape
3832// claims one declaration range. The count is deterministic for a given source,
3833// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3834// pins it directly instead of timing the walk, the way #2358 pinned the
3835// `remove_code_unit` scan.
3836#[cfg(any(test, feature = "test-support"))]
3837thread_local! {
3838    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3839        const { std::cell::Cell::new(0) };
3840}
3841
3842/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3843/// the calling thread since the last
3844/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3845#[cfg(any(test, feature = "test-support"))]
3846#[doc(hidden)]
3847pub fn recovered_class_body_node_visits_for_test() -> usize {
3848    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3849}
3850
3851/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3852#[cfg(any(test, feature = "test-support"))]
3853#[doc(hidden)]
3854pub fn reset_recovered_class_body_node_visits_for_test() {
3855    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3856}
3857
3858#[cfg(any(test, feature = "test-support"))]
3859fn record_recovered_class_body_visit() {
3860    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3861}
3862
3863#[cfg(not(any(test, feature = "test-support")))]
3864fn record_recovered_class_body_visit() {}
3865
3866/// Whether a recovered class shape named `identifier` owns `range`, and if so
3867/// whether that shape has a body.
3868///
3869/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3870/// forward declaration, and `None` means no recovered shape claims the range,
3871/// so the caller reads the plain `class_specifier` family instead. This is the
3872/// single definition of "does a recovered class have a body": the resolver's
3873/// declaration-strength answer and the navigation occurrence role both read it,
3874/// so an export-macro class is a definition on both paths.
3875///
3876/// The walk follows only the nodes whose span covers `range.start_byte`, which
3877/// is every node that can answer. Each recovered shape reports a range that
3878/// starts at the node's own start byte (the function-like export pair, whose
3879/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3880/// class, which the caller gates on an equal start) or at a token inside the
3881/// node (the embedded export class, keyed on its `class` token, and the
3882/// exported class wrapper, gated on containment here), and every one of them is
3883/// accepted only on an exact match with `range`. Descending everywhere instead
3884/// made one declaration-strength question cost a full pass over the file, so
3885/// asking it once per reference was quadratic in file size: 80% of the 385 s
3886/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3887/// (#1496).
3888pub(crate) fn recovered_class_body_at(
3889    recovered_export_classes: &CppRecoveredExportClassIndex,
3890    root: Node<'_>,
3891    source: &str,
3892    identifier: &str,
3893    range: &Range,
3894) -> Option<bool> {
3895    let covers_range_start = |node: &Node<'_>| {
3896        node.start_byte() <= range.start_byte
3897            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3898    };
3899    let mut stack = vec![root];
3900    let mut saw_forward = false;
3901    while let Some(node) = stack.pop() {
3902        record_recovered_class_body_visit();
3903        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3904        // so an unequal start settles it before the recovery reads the node's
3905        // children at all.
3906        if (node.start_byte() == range.start_byte
3907            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3908            || recovered_export_classes.claims(node, identifier, range)
3909            || (node.start_byte() == range.start_byte
3910                && recovered_fragmented_class_has_body(node, source, identifier, range))
3911        {
3912            return Some(true);
3913        }
3914        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3915            return Some(true);
3916        }
3917        // Macro-decorated exported classes are recovered from a malformed
3918        // function_definition/declaration wrapper. Their indexed class range starts at
3919        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3920        // may also extend the indexed range beyond the wrapper through trailing class
3921        // fragments. Match the structured container that owns the range start by its
3922        // recovered name instead of requiring identical boundaries.
3923        if node.start_byte() <= range.start_byte
3924            && range.start_byte < node.end_byte()
3925            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3926        {
3927            if has_body {
3928                return Some(true);
3929            }
3930            saw_forward = true;
3931            continue;
3932        }
3933        let mut cursor = node.walk();
3934        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3935    }
3936    saw_forward.then_some(false)
3937}
3938
3939/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3940/// head of `identifier`'s member list at `range`.
3941///
3942/// An aggregate whose member list opens with a field-list macro invocation
3943/// never forms a specifier, so the ordinary strength read finds no specifier
3944/// node at the indexed range and answers `Unknown`. A complete definition
3945/// recovered this way then loses the defining-type choice to a forward
3946/// declaration of the same tag in another header, and every member the
3947/// definition declares becomes unreachable through it: libuv's
3948/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3949/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3950/// proves the body, so it answers for its own ranges.
3951fn recovered_collapsed_aggregate_has_body(
3952    node: Node<'_>,
3953    source: &str,
3954    identifier: &str,
3955    range: &Range,
3956) -> bool {
3957    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3958        head.key.start_byte() == range.start_byte
3959            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3960    };
3961    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3962        return true;
3963    }
3964    if !node.is_error() {
3965        return false;
3966    }
3967    let mut cursor = node.walk();
3968    let children = node.children(&mut cursor).collect::<Vec<_>>();
3969    children
3970        .iter()
3971        .enumerate()
3972        .filter(|(_, child)| {
3973            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3974        })
3975        .any(|(index, _)| {
3976            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3977        })
3978}
3979
3980/// Whether `node` is the base type displaced into the declarator field of an
3981/// export-macro class that tree-sitter represented as a declaration or
3982/// function definition.
3983///
3984/// Declaration extraction already recovers this exact malformed envelope as a
3985/// class and records the declarator as its base. Reference extraction must use
3986/// the same structural fact instead of treating the node as a function name.
3987pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3988    if !matches!(
3989        node.kind(),
3990        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3991    ) {
3992        return false;
3993    }
3994    if let Some(function) = node.parent().filter(|parent| {
3995        parent.kind() == "function_definition"
3996            && parent
3997                .child_by_field_name("declarator")
3998                .is_some_and(|declarator| same_node(declarator, node))
3999    }) {
4000        return recover_exported_class_function_definition(function, source)
4001            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
4002    }
4003    let Some(initializer) = node.parent().filter(|parent| {
4004        parent.kind() == "init_declarator"
4005            && parent
4006                .child_by_field_name("declarator")
4007                .is_some_and(|declarator| same_node(declarator, node))
4008    }) else {
4009        return false;
4010    };
4011    initializer
4012        .parent()
4013        .filter(|parent| parent.kind() == "declaration")
4014        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
4015        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
4016}
4017
4018/// Recover the class item from a region reparse that still carries the
4019/// sentinel's synthetic function envelope.  An unknown class attribute can
4020/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
4021/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
4022/// then nested below that function, so direct class-child lookup is not enough.
4023struct CppSentinelReparsedClass<'tree> {
4024    declaration_node: Node<'tree>,
4025    name: String,
4026    body: Node<'tree>,
4027    raw_supertypes: Option<Vec<String>>,
4028    base_facts: Option<Vec<CppBaseSpecifierFact>>,
4029}
4030
4031fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4032    let mut cursor = root.walk();
4033    root.named_children(&mut cursor)
4034        .find(|child| child.kind() != "comment")
4035        .filter(|child| child.kind() == "template_declaration")
4036}
4037
4038fn cpp_sentinel_reparsed_class<'tree>(
4039    root: Node<'tree>,
4040    template_node: Option<Node<'tree>>,
4041    source: &str,
4042    ancestry: &ParentIndex<'tree>,
4043) -> Option<CppSentinelReparsedClass<'tree>> {
4044    let container = template_node.unwrap_or(root);
4045    let mut cursor = container.walk();
4046    for child in container.named_children(&mut cursor) {
4047        if matches!(
4048            child.kind(),
4049            "class_specifier" | "struct_specifier" | "union_specifier"
4050        ) {
4051            let name = class_like_name(child, source, ancestry)?;
4052            let body = cpp_body_node(child)?;
4053            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4054                .then(|| extract_cpp_supertypes(child, source));
4055            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4056                .then(|| extract_cpp_base_facts(child, source));
4057            return Some(CppSentinelReparsedClass {
4058                declaration_node: child,
4059                name,
4060                body,
4061                raw_supertypes,
4062                base_facts,
4063            });
4064        }
4065        if child.kind() == "declaration"
4066            && let Some(class_node) = first_class_like_child(child)
4067        {
4068            let name = class_like_name(class_node, source, ancestry)?;
4069            let body = cpp_body_node(class_node)?;
4070            let raw_supertypes =
4071                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4072                    .then(|| extract_cpp_supertypes(class_node, source));
4073            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4074                .then(|| extract_cpp_base_facts(class_node, source));
4075            return Some(CppSentinelReparsedClass {
4076                declaration_node: class_node,
4077                name,
4078                body,
4079                raw_supertypes,
4080                base_facts,
4081            });
4082        }
4083        // Only when the nested class item carries its own body. A bodyless
4084        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
4085        // a function definition -- is the export-macro shape recovered by the
4086        // next arm, and must fall through to it rather than abort the search.
4087        if child.kind() == "function_definition"
4088            && let Some(class_node) = first_class_like_child(child)
4089            && let Some(body) = cpp_body_node(class_node)
4090            && let Some(name) = class_like_name(class_node, source, ancestry)
4091        {
4092            let raw_supertypes =
4093                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4094                    .then(|| extract_cpp_supertypes(class_node, source));
4095            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4096                .then(|| extract_cpp_base_facts(class_node, source));
4097            return Some(CppSentinelReparsedClass {
4098                declaration_node: class_node,
4099                name,
4100                body,
4101                raw_supertypes,
4102                base_facts,
4103            });
4104        }
4105        if child.kind() == "function_definition"
4106            && let Some((_, name, raw_supertypes, base_facts)) =
4107                recover_exported_class_function_definition(child, source)
4108        {
4109            let body = cpp_body_node(child)?;
4110            return Some(CppSentinelReparsedClass {
4111                declaration_node: child,
4112                name,
4113                body,
4114                raw_supertypes,
4115                base_facts,
4116            });
4117        }
4118    }
4119    None
4120}
4121
4122fn recovered_postfix_export_macro_base(
4123    node: Node<'_>,
4124    type_node: Node<'_>,
4125    declarator: Node<'_>,
4126    source: &str,
4127) -> Option<String> {
4128    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4129        .and_then(|base| recovered_malformed_base_name(base, source))
4130}
4131
4132fn recovered_postfix_export_macro_base_node<'tree>(
4133    node: Node<'tree>,
4134    type_node: Node<'tree>,
4135    declarator: Node<'tree>,
4136    source: &str,
4137) -> Option<Node<'tree>> {
4138    let mut cursor = node.walk();
4139    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4140        child.kind() == "ERROR"
4141            && child.start_byte() >= type_node.end_byte()
4142            && child.end_byte() <= declarator.start_byte()
4143            && postfix_export_macro_inheritance(*child, source)
4144    });
4145    malformed_clauses.next()?;
4146    if malformed_clauses.next().is_some() {
4147        return None;
4148    }
4149    Some(declarator)
4150}
4151
4152fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4153    let mut macro_count = 0;
4154    let mut colon_count = 0;
4155    let mut access_count = 0;
4156    for index in 0..node.child_count() {
4157        let Some(child) = node.child(index) else {
4158            return false;
4159        };
4160        match child.kind() {
4161            "identifier" | "type_identifier" if child.is_named() => {
4162                let candidate = normalize_cpp_whitespace(node_text(child, source));
4163                if !cpp_export_macro_token(&candidate) {
4164                    return false;
4165                }
4166                macro_count += 1;
4167            }
4168            ":" if !child.is_named() => colon_count += 1,
4169            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4170            _ => return false,
4171        }
4172    }
4173    macro_count == 1 && colon_count == 1 && access_count == 1
4174}
4175
4176fn recovered_single_base_after_declarator_node<'tree>(
4177    node: Node<'tree>,
4178    declarator: Node<'tree>,
4179    source: &str,
4180) -> Option<Node<'tree>> {
4181    let body_start = node
4182        .child_by_field_name("body")
4183        .map(|body| body.start_byte())
4184        .unwrap_or(node.end_byte());
4185    let mut cursor = node.walk();
4186    let mut bases = node
4187        .named_children(&mut cursor)
4188        .filter(|child| {
4189            child.kind() == "ERROR"
4190                && child.start_byte() >= declarator.end_byte()
4191                && child.end_byte() <= body_start
4192        })
4193        .filter_map(|error| {
4194            displaced_exported_class_name(error, source)?;
4195            let mut error_cursor = error.walk();
4196            error
4197                .named_children(&mut error_cursor)
4198                .find(|child| recovered_malformed_base_name(*child, source).is_some())
4199        });
4200    let base = bases.next()?;
4201    bases.next().is_none().then_some(base)
4202}
4203
4204fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4205    (0..node.child_count()).any(|index| {
4206        node.child(index).is_some_and(|child| {
4207            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4208        })
4209    })
4210}
4211
4212fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4213    matches!(name, "public" | "protected" | "private")
4214}
4215
4216pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4217    recover_exported_class_function_definition(node, source).is_some()
4218}
4219
4220fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4221    matches!(
4222        kind,
4223        "ERROR"
4224            | "preproc_if"
4225            | "preproc_ifdef"
4226            | "preproc_ifndef"
4227            | "preproc_else"
4228            | "preproc_elif"
4229    ) || (kind == "labeled_statement" && in_class_scope)
4230}
4231
4232pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4233    if node.kind() != "declaration" {
4234        return false;
4235    }
4236    let mut ancestor = node.parent();
4237    while let Some(container) = ancestor {
4238        match container.kind() {
4239            "compound_statement" => {
4240                return container.parent().is_some_and(|class_container| {
4241                    is_recovered_exported_class_container(class_container, source)
4242                });
4243            }
4244            // These containers preserve ScopeInfo in visit_node. declaration_list is
4245            // the body container selected for a linkage specification.
4246            "template_declaration" | "linkage_specification" | "declaration_list" => {}
4247            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4248            _ => return false,
4249        }
4250        ancestor = container.parent();
4251    }
4252    false
4253}
4254
4255fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4256    let body_start = node
4257        .child_by_field_name("body")
4258        .map(|body| body.start_byte())
4259        .unwrap_or(node.end_byte());
4260    let mut stack = Vec::new();
4261    for index in (0..node.named_child_count()).rev() {
4262        let Some(child) = node.named_child(index) else {
4263            continue;
4264        };
4265        if child.start_byte() >= body_start {
4266            continue;
4267        }
4268        stack.push(child);
4269    }
4270
4271    let mut best = None;
4272    while let Some(current) = stack.pop() {
4273        if matches!(current.kind(), "identifier" | "type_identifier") {
4274            let name = normalize_cpp_whitespace(node_text(current, source));
4275            if !name.is_empty()
4276                && !cpp_export_macro_token(&name)
4277                && !matches!(name.as_str(), "class" | "struct" | "union")
4278            {
4279                best = Some(name);
4280            }
4281            continue;
4282        }
4283
4284        for index in (0..current.named_child_count()).rev() {
4285            if let Some(child) = current.named_child(index)
4286                && child.start_byte() < body_start
4287            {
4288                stack.push(child);
4289            }
4290        }
4291    }
4292    best
4293}
4294
4295fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4296    if node.kind() == "declaration"
4297        && node
4298            .child_by_field_name("type")
4299            .or_else(|| first_class_like_child(node))
4300            .is_some_and(|type_node| {
4301                matches!(
4302                    type_node.kind(),
4303                    "class_specifier" | "struct_specifier" | "union_specifier"
4304                )
4305            })
4306        && let Some(name) = node
4307            .child_by_field_name("declarator")
4308            .and_then(|declarator| declarator_name_from_node(declarator, source))
4309        && !cpp_export_macro_token(&name)
4310    {
4311        return Some(name);
4312    }
4313
4314    if node.kind() == "function_definition"
4315        && node.child_by_field_name("type").is_some_and(|type_node| {
4316            matches!(
4317                type_node.kind(),
4318                "class_specifier" | "struct_specifier" | "union_specifier"
4319            )
4320        })
4321        && let Some(name) = node
4322            .child_by_field_name("declarator")
4323            .and_then(|declarator| direct_identifier_name(declarator, source))
4324        && !cpp_export_macro_token(&name)
4325    {
4326        return Some(name);
4327    }
4328
4329    let class_node = if matches!(
4330        node.kind(),
4331        "class_specifier" | "struct_specifier" | "union_specifier"
4332    ) {
4333        node
4334    } else {
4335        first_class_like_child(node)?
4336    };
4337    class_like_name_from_children(class_node, source)
4338}
4339
4340/// Normalize the text of a directly selected C++ identifier node.
4341pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4342    if !matches!(
4343        node.kind(),
4344        "identifier" | "field_identifier" | "type_identifier"
4345    ) {
4346        return None;
4347    }
4348    let name = normalize_cpp_whitespace(node_text(node, source));
4349    (!name.is_empty()).then_some(name)
4350}
4351
4352fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4353    if let Some(name) = direct_identifier_name(node, source) {
4354        return Some(name);
4355    }
4356    let mut cursor = node.walk();
4357    node.named_children(&mut cursor)
4358        .find_map(|child| declarator_name_from_node(child, source))
4359}
4360
4361fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4362    let mut cursor = node.walk();
4363    node.named_children(&mut cursor).find(|child| {
4364        matches!(
4365            child.kind(),
4366            "class_specifier" | "struct_specifier" | "union_specifier"
4367        )
4368    })
4369}
4370
4371/// Push a container's children as a `Siblings` cursor rather than snapshotting
4372/// them all with one shared scope: children are visited one at a time so a
4373/// `using namespace X;` sibling can affect the scope threaded to the siblings
4374/// that textually follow it (issue #1093).
4375fn push_cpp_container_work<'tree>(
4376    node: Node<'tree>,
4377    scope: ScopeInfo,
4378    stack: &mut Vec<CppWork<'tree>>,
4379) {
4380    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4381}
4382
4383/// Materialize one selected named-child range with a tree-sitter cursor. The
4384/// cursor advances linearly across the parent's concrete children; repeatedly
4385/// asking for `named_child(index)` is quadratic on very wide generated nodes.
4386fn push_cpp_sibling_range<'tree>(
4387    parent: Node<'tree>,
4388    start_index: usize,
4389    end_index: usize,
4390    scope: ScopeInfo,
4391    stack: &mut Vec<CppWork<'tree>>,
4392) {
4393    let mut cursor = parent.walk();
4394    let children = parent
4395        .named_children(&mut cursor)
4396        .skip(start_index)
4397        .take(end_index.saturating_sub(start_index))
4398        .collect::<Vec<_>>()
4399        .into_iter();
4400    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4401}
4402
4403/// Advance a `Siblings` cursor by one child: dispatch the current child under
4404/// the scope accumulated from its *earlier* siblings, then push a
4405/// continuation for the remaining siblings carrying the scope updated for
4406/// *this* child (only `using namespace X;` directives change it). Pushing the
4407/// continuation before the current child's own node work means the current
4408/// child's subtree fully drains (LIFO) before the next sibling is visited,
4409/// preserving left-to-right order.
4410fn advance_cpp_siblings<'tree>(
4411    mut siblings: CppSiblingsWork<'tree>,
4412    source: &str,
4413    stack: &mut Vec<CppWork<'tree>>,
4414) {
4415    let Some(child) = siblings.children.next() else {
4416        return;
4417    };
4418    let current_scope = siblings.scope.clone();
4419    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4420        siblings.scope.visible_using_namespaces.push(namespace);
4421    }
4422    push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4423    if !siblings.children.as_slice().is_empty() {
4424        stack.push(CppWork::Siblings(siblings));
4425    }
4426    stack.push(CppWork::Node(CppNodeWork {
4427        node: child,
4428        scope: current_scope,
4429    }));
4430}
4431
4432/// The namespace target of a `using namespace X;` directive, or `None` for
4433/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4434/// kind. Distinguished structurally by the presence of the grammar's literal
4435/// `namespace` keyword token among the node's children -- not by inspecting
4436/// source text -- so it never misreads a member-importing using-declaration
4437/// as a namespace directive.
4438fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4439    if node.kind() != "using_declaration" {
4440        return None;
4441    }
4442    let mut cursor = node.walk();
4443    let is_namespace_directive = node
4444        .children(&mut cursor)
4445        .any(|child| child.kind() == "namespace");
4446    if !is_namespace_directive {
4447        return None;
4448    }
4449    let target = node.named_child(0)?;
4450    // A leading `::` is the explicit-global marker, not part of the namespace
4451    // path (`using namespace ::std::chrono;`). Drop that AST token before
4452    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4453    // keeps: storing the marker verbatim desynced the legacy package string from
4454    // the FqName bridge, which splits on `::` and drops the empty leading
4455    // component, tripping the package/short boundary assert when a bare-owner
4456    // out-of-line definition borrowed the directive's namespace (#1093 path).
4457    let start = target
4458        .child(0)
4459        .filter(|child| !child.is_named() && child.kind() == "::")
4460        .map_or(target.start_byte(), |marker| marker.end_byte());
4461    let text = normalize_cpp_whitespace(
4462        source
4463            .get(start..target.end_byte())
4464            .expect("using-directive target covers one source range"),
4465    );
4466    (!text.is_empty()).then_some(text)
4467}
4468
4469/// Record `node` in `scope` when it is a namespace-scope `using ns::Type;`
4470/// declaration, so later siblings see the binding it introduces.
4471///
4472/// The target is read from the declaration's structured qualified name. A
4473/// using directive (`using namespace ns;`), a `using enum`, a member using
4474/// inside a class body, and a target with a template-id component introduce
4475/// no class binding this extraction can use, so they are skipped. A relative
4476/// target inside a namespace resolves against that namespace, dropping a
4477/// prefix that re-states it, the same reading `split_cpp_name` gives a
4478/// written out-of-line qualifier: a member definition must sit in a namespace
4479/// that encloses its class.
4480fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4481    if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4482        return;
4483    }
4484    let mut cursor = node.walk();
4485    if node
4486        .children(&mut cursor)
4487        .any(|child| matches!(child.kind(), "namespace" | "enum"))
4488    {
4489        return;
4490    }
4491    let Some((components, explicitly_global)) = node
4492        .named_child(0)
4493        .and_then(|target| structured_cpp_qualified_components(target, source))
4494    else {
4495        return;
4496    };
4497    let Some((name, namespace)) = components.split_last() else {
4498        return;
4499    };
4500    if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4501        return;
4502    }
4503    let namespace = namespace
4504        .iter()
4505        .map(|component| component.name.as_str())
4506        .collect::<Vec<_>>();
4507    let target_package = if explicitly_global || scope.package_name.is_empty() {
4508        namespace.join("::")
4509    } else {
4510        let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4511        std::iter::once(scope.package_name.as_str())
4512            .chain(relative.iter().copied())
4513            .collect::<Vec<_>>()
4514            .join("::")
4515    };
4516    Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4517        scope_package: scope.package_name.clone(),
4518        name: name.name.clone(),
4519        target_package,
4520    });
4521}
4522
4523/// Tokens that open a construct only a matching later token closes.
4524const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4525
4526/// The opening tokens of the constructs a C/C++ parse left open at the end of
4527/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4528/// node.
4529///
4530/// The grammar has no preprocessor, so a construct opened in one conditional
4531/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4532/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4533/// One such pair repairs with a single MISSING token. With more of them, the
4534/// parse version tree-sitter keeps reaches the end of input in its error
4535/// state, and tree-sitter wraps every subtree on that version's stack in one
4536/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4537/// The root's children are then exactly that stack: completed subtrees, and
4538/// the bare tokens of the constructs still waiting to be reduced when input
4539/// ran out. A reduced construct is a node of its own, so a bare opener among
4540/// the root's children is one the parse never closed. tdlib/td's copy of the
4541/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4542/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4543/// at byte 19,718.
4544pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4545    assert!(
4546        root.parent().is_none(),
4547        "only a tree's root records what its parse left open"
4548    );
4549    if !root.is_error() {
4550        return Vec::new();
4551    }
4552    children_iter(root)
4553        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4554        .collect()
4555}
4556
4557/// Record every declaration of `parsed` that has a range inside `recovery`
4558/// as recovered, unless a narrower recovery already claimed it, and mark the
4559/// reading partial when any declaration lies there (#3501).
4560///
4561/// `recovery` runs from the outermost construct the parse left open to the
4562/// end of the file. Everything before that construct was completed at file
4563/// scope, as in any parse. Everything after it sat on the parse stack above
4564/// an open construct when input ran out, so the scope and nesting the walk
4565/// gave it are a recovery claim rather than a reading of a completed tree,
4566/// and the file's analysis is partial whichever recovery recorded it.
4567fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4568    let recorded: HashSet<CodeUnit> = parsed
4569        .materialization_records
4570        .iter()
4571        .filter_map(|record| match record {
4572            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4573            _ => None,
4574        })
4575        .collect();
4576    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4577        .declarations()
4578        .iter()
4579        .filter_map(|unit| {
4580            let ranges = parsed.declaration_ranges(unit);
4581            let first = ranges.iter().map(|range| range.start_byte).min()?;
4582            let last = ranges.iter().map(|range| range.start_byte).max()?;
4583            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4584        })
4585        .collect();
4586    if left_open.is_empty() {
4587        return;
4588    }
4589    parsed.completeness = ParseCompleteness::Partial;
4590    left_open.retain(|(_, unit)| !recorded.contains(unit));
4591    left_open.sort();
4592    for (_, unit) in left_open {
4593        parsed
4594            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4595    }
4596}
4597
4598/// Extract all requested dialect readings from one primary AST event stream.
4599/// Declaration schedulers retain their own admission, recovery and sibling order;
4600/// structural facts, includes and identifiers are interpreted once for the tree.
4601pub(crate) fn parse_cpp_readings<'tree>(
4602    file: &ProjectFile,
4603    source: &str,
4604    root: Node<'tree>,
4605    ancestry: &ParentIndex<'tree>,
4606    dialects: &[LanguageDialect],
4607) -> Vec<ParsedFile> {
4608    parse_cpp_readings_with_object_macro_fields(
4609        file,
4610        source,
4611        root,
4612        ancestry,
4613        dialects,
4614        HashMap::default(),
4615    )
4616}
4617
4618pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4619    file: &ProjectFile,
4620    source: &str,
4621    root: Node<'tree>,
4622    ancestry: &ParentIndex<'tree>,
4623    dialects: &[LanguageDialect],
4624    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4625) -> Vec<ParsedFile> {
4626    assert!(
4627        dialects.len() == 1
4628            || (dialects.len() == 2
4629                && dialects[0] != LanguageDialect::CppC
4630                && dialects[1] == LanguageDialect::CppC),
4631        "one primary dialect, optionally followed by its C alternative"
4632    );
4633    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4634    let properties = RefCell::new(HashMap::default());
4635    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4636    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4637    let mut using_namespaces = Vec::new();
4638    let mut namespace_aliases = Vec::new();
4639    let mut readings: Vec<_> = dialects
4640        .iter()
4641        .map(|_| ParsedFile::new(String::new()))
4642        .collect();
4643    let mut visitors: Vec<_> = readings
4644        .iter_mut()
4645        .zip(dialects)
4646        .map(|(parsed, dialect)| CppVisitor {
4647            file,
4648            source,
4649            parsed,
4650            source_collector: &collector,
4651            source_properties: &properties,
4652            source_dependencies: &dependencies,
4653            primary_ancestry: ancestry,
4654            primary_root_id: root.id(),
4655            source_links: HashMap::default(),
4656            source_declarations: HashSet::default(),
4657            preprocessor_context_cache: Default::default(),
4658            c_tag_semantics: *dialect == LanguageDialect::CppC,
4659            c_tag_scope_witness: false,
4660            recovered_class_sibling_scopes: HashMap::default(),
4661            consumed_fragment_regions: Vec::new(),
4662            orphaned_namespaces: &orphaned_namespaces,
4663            partitioned_regions: Vec::new(),
4664            namespace_forward_scans: HashMap::default(),
4665            recovery_captures: Vec::new(),
4666            active_source_context: None,
4667            object_macro_fields: object_macro_fields.clone(),
4668            ambiguous_object_macro_fields: HashSet::default(),
4669        })
4670        .collect();
4671    let mut declaration_stacks: Vec<_> = dialects
4672        .iter()
4673        .map(|_| {
4674            vec![CppWork::Container(CppContainer {
4675                node: root,
4676                scope: ScopeInfo {
4677                    package_name: String::new(),
4678                    module: None,
4679                    class_unit: None,
4680                    template_signature: None,
4681                    template_metadata: None,
4682                    declarations_are_fields: false,
4683                    recovered_specialization_member_scope: false,
4684                    visible_using_namespaces: Vec::new(),
4685                    local_block: None,
4686                    visible_using_declarations: Arc::default(),
4687                },
4688            })]
4689        })
4690        .collect();
4691    let kinds = CompiledKinds::compile(
4692        &tree_sitter_cpp::LANGUAGE.into(),
4693        CPP_STRUCTURAL_SPEC.kind_table(),
4694    );
4695    // Forward macro names affect calls before their definitions and require the
4696    // existing bounded preprocessor-only context prepass.
4697    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4698    let mut structural = StructuralFactCollector::new(
4699        &CPP_STRUCTURAL_SPEC,
4700        source,
4701        &context,
4702        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4703        usize::MAX,
4704        None,
4705    );
4706    let mut imports = Vec::new();
4707    let mut includes = Vec::new();
4708    let mut expressions = Vec::new();
4709    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4710    let mut statement_facts = StatementFactRows::default();
4711    let mut identifiers = HashSet::default();
4712    let mut entered = HashSet::default();
4713    let mut primary_ordinals = HashMap::default();
4714    let mut stack = vec![(root, None)];
4715    while let Some((node, parent)) = stack.pop() {
4716        assert!(entered.insert(node.id()), "one primary event per AST node");
4717        primary_ordinals.insert(node.id(), primary_ordinals.len());
4718        dependencies.borrow_mut().primary.enter(node, source);
4719        let mut child_parent = parent;
4720        if node.is_named() {
4721            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4722            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4723                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4724            }
4725            if let Some(alias) = crate::source_facts::capture_namespace_alias(
4726                node,
4727                source,
4728                ancestry,
4729                &orphaned_namespaces,
4730                &mut collector.borrow_mut(),
4731            ) {
4732                namespace_aliases.push(alias);
4733            }
4734            if let Some(raw_kind) = kinds.kind_of(&node)
4735                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4736            {
4737                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4738                    node,
4739                    raw_kind,
4740                    parent.map(|id| structural.normalized_kind(id)),
4741                    source,
4742                    &context,
4743                );
4744                let mut source_facts = collector.borrow_mut();
4745                let id = structural
4746                    .enter(node, kind, parent, &mut source_facts)
4747                    .expect("unbounded C++ structural collector");
4748                let mut sink = structural.role_sink(&mut source_facts);
4749                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4750                structural
4751                    .accept_roles(id, sink.into_parts())
4752                    .expect("unbounded C++ structural roles");
4753                child_parent = Some(id);
4754            }
4755            if let Some(fact) = crate::source_facts::capture_expression_fact(
4756                node,
4757                source,
4758                collector.borrow_mut().intern_node(node),
4759                &named_record_sizes,
4760            ) {
4761                expressions.push(fact);
4762            }
4763            if node.kind() == "preproc_include" {
4764                let raw = normalize_cpp_whitespace(node_text(node, source));
4765                if !raw.is_empty() {
4766                    let occurrence = collector.borrow_mut().intern_node(node);
4767                    let target = node.child_by_field_name("path").and_then(|path| {
4768                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4769                        let target = collector.borrow_mut().intern_node(path);
4770                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4771                            declaration: occurrence,
4772                            target,
4773                            path: value.to_owned(),
4774                            quoted,
4775                        });
4776                        Some(target)
4777                    });
4778                    imports.push(SourceImportFact::from_import(
4779                        cpp_include_info(raw),
4780                        occurrence,
4781                        target,
4782                        None,
4783                        Vec::new(),
4784                    ));
4785                }
4786            }
4787            if matches!(
4788                node.kind(),
4789                "type_identifier" | "identifier" | "qualified_identifier"
4790            ) {
4791                let text = node_text(node, source).trim();
4792                if !text.is_empty() {
4793                    identifiers.insert(text.to_owned());
4794                }
4795            }
4796        }
4797        for index in 0..visitors.len() {
4798            // Delay the alternative policy until the primary walk encounters a
4799            // tag whose scope can differ. Its saved declaration stack catches up
4800            // through already-entered events, including recovery, without a
4801            // second AST traversal. A false witness performs no C declaration work.
4802            if index != 0 && !visitors[0].c_tag_scope_witness {
4803                continue;
4804            }
4805            let visitor = &mut visitors[index];
4806            advance_primary_declarations(
4807                visitor,
4808                &mut declaration_stacks[index],
4809                &entered,
4810                ancestry,
4811            );
4812            visitor.drain_partitioned_regions();
4813        }
4814        let mut cursor = node.walk();
4815        let children: Vec<_> = node
4816            .children(&mut cursor)
4817            .map(|child| (child, child_parent))
4818            .collect();
4819        stack.extend(children.into_iter().rev());
4820    }
4821    let reading_count = if visitors[0].c_tag_scope_witness {
4822        visitors.len()
4823    } else {
4824        1
4825    };
4826    visitors.truncate(reading_count);
4827    declaration_stacks.truncate(reading_count);
4828    assert!(
4829        declaration_stacks.iter().all(Vec::is_empty),
4830        "all primary declaration events were consumed"
4831    );
4832    for visitor in &mut visitors {
4833        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4834            .into_iter()
4835            .flat_map(|(unit, declarations)| {
4836                declarations
4837                    .into_iter()
4838                    .map(move |declaration| (declaration, unit.clone()))
4839            })
4840            .collect();
4841    }
4842    let dialect_declarations: Vec<_> = visitors
4843        .iter()
4844        .map(|visitor| visitor.source_declarations.clone())
4845        .collect();
4846    drop(visitors);
4847    readings.truncate(reading_count);
4848    dependencies
4849        .borrow_mut()
4850        .finish_primary(&mut properties.borrow_mut());
4851    // Preserve the existing malformed quoted-directive recovery, but publish its
4852    // explicit source occurrence into the same import family as parsed directives.
4853    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4854    let primary_occurrence_ordinals: HashMap<_, _> = collector
4855        .borrow()
4856        .primary_node_occurrences()
4857        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4858        .collect();
4859    let occurrences = collector.into_inner().finish();
4860    let generic_imports: Vec<_> = (0..imports.len())
4861        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4862        .collect();
4863    let source_facts = ParsedSourceFacts {
4864        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4865            includes,
4866            declarations: {
4867                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4868                facts.sort_by_key(|fact| fact.declaration.get());
4869                facts
4870            },
4871            using_namespaces,
4872            namespace_aliases,
4873            expressions,
4874            statement_facts,
4875        }),
4876        go: None,
4877        java: None,
4878        js_ts: None,
4879        php: None,
4880        scala: None,
4881        ruby: None,
4882        python: None,
4883        source_bytes: source.len(),
4884        occurrences,
4885        structural: structural
4886            .finish(root)
4887            .expect("C++ structural collection finishes"),
4888        native_site_occurrences: Vec::new(),
4889        native_declaration_sources: Vec::new(),
4890        declaration_visibilities: None,
4891        rust_declaration_properties: Vec::new(),
4892        rust_modules: None,
4893        rust_types: Vec::new(),
4894        rust_items: Default::default(),
4895        imports,
4896        generic_imports,
4897        rust_import_contexts: Vec::new(),
4898    };
4899    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4900        parsed.finalize_deferred_replacements();
4901        let links = std::mem::take(&mut parsed.source_declaration_units);
4902        parsed.source_declaration_units = links
4903            .into_iter()
4904            .filter(|(_, unit)| parsed.contains_declaration(unit))
4905            .collect();
4906        parsed.imports = source_facts
4907            .imports
4908            .iter()
4909            .map(|import| import.import_info(&source_facts.occurrences))
4910            .collect();
4911        parsed.type_identifiers = identifiers.clone();
4912        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4913            &source_facts,
4914            &declarations,
4915            &primary_occurrence_ordinals,
4916            &mut parsed.source_declaration_units,
4917            &mut parsed.source_declaration_metadata,
4918        ));
4919    }
4920    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4921        // The recovery runs to the end of the file, which is how a reader of
4922        // the record tells this recovery from a local one (#3501).
4923        let recovery = Range {
4924            start_byte: outermost.start_byte(),
4925            end_byte: source.len(),
4926            start_line: outermost.start_position().row + 1,
4927            end_line: root.end_position().row + 1,
4928        };
4929        for parsed in &mut readings {
4930            record_declarations_left_open(parsed, recovery);
4931        }
4932    }
4933    readings
4934}
4935
4936fn advance_primary_declarations<'tree>(
4937    visitor: &mut CppVisitor<'_, '_>,
4938    stack: &mut Vec<CppWork<'tree>>,
4939    entered: &HashSet<usize>,
4940    ancestry: &ParentIndex<'tree>,
4941) {
4942    while let Some(work) = stack.pop() {
4943        match work {
4944            CppWork::Container(container) => {
4945                push_cpp_container_work(container.node, container.scope, stack)
4946            }
4947            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
4948            CppWork::Node(work) => {
4949                if !entered.contains(&work.node.id()) {
4950                    stack.push(CppWork::Node(work));
4951                    return;
4952                }
4953                if !visitor.node_is_inside_consumed_fragment(work.node) {
4954                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
4955                }
4956            }
4957        }
4958    }
4959}
4960
4961#[derive(Clone)]
4962struct CppActiveSourceContext {
4963    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
4964    namespace: Option<String>,
4965    child_namespace: Option<String>,
4966    outer_lexical_scope: Vec<String>,
4967    child_lexical_scope: Vec<String>,
4968    family: Option<SourceOccurrenceId>,
4969    exhaustive_family: Option<SourceOccurrenceId>,
4970    callable_guard_completion_byte: Option<usize>,
4971}
4972
4973/// One owned container reparse deferred until the primary declaration walk has
4974/// reached its source position. The tree and context are owned so the deferred
4975/// walk never retains a `Node` borrowed from a temporary recovery tree.
4976pub struct CppPartitionedRegion {
4977    tree: Tree,
4978    range: std::ops::Range<usize>,
4979    scope: ScopeInfo,
4980    recovery: Range,
4981    source_context: Option<CppActiveSourceContext>,
4982}
4983
4984pub struct CppVisitor<'a, 'source> {
4985    pub file: &'a ProjectFile,
4986    pub source: &'source str,
4987    pub parsed: &'a mut ParsedFile,
4988    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
4989    source_properties: &'a RefCell<
4990        HashMap<
4991            SourceDeclarationId,
4992            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
4993        >,
4994    >,
4995    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
4996    pub primary_ancestry: &'a ParentIndex<'a>,
4997    pub primary_root_id: usize,
4998    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
4999    source_declarations: HashSet<SourceDeclarationId>,
5000    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5001    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
5002    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
5003    ///
5004    /// C has no nested tag scope: a struct/union/enum tag declared inside
5005    /// another aggregate's member list has the scope of the outer declaration
5006    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
5007    /// therefore declares a file-scope `inner` that a later file-scope
5008    /// `struct inner *p;` legitimately references, where C++ would make the
5009    /// same shape a nested class `outer::inner`. Headers carry no compilation
5010    /// language of their own and keep the conservative C++ interpretation.
5011    pub c_tag_semantics: bool,
5012    /// Sticky evidence that this C++ walk reached a declaration whose scope
5013    /// would change under C tag semantics, including visitor recovery work.
5014    pub(crate) c_tag_scope_witness: bool,
5015    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5016    /// Byte regions whose contents were re-owned by a fragmented export-class
5017    /// recovery (#938): the scattered members between the fragmented
5018    /// declaration and its displaced closing brace are indexed as members of
5019    /// the recovered class by the region reparse, so the ordinary sibling walk
5020    /// must not ALSO index them as top-level declarations (that double-indexing
5021    /// made a scattered nested class ambiguous between `Inner` and
5022    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
5023    /// linear scan at visit time is fine.
5024    pub consumed_fragment_regions: Vec<(usize, usize)>,
5025    /// Lexical namespace corrections and brace boundaries for declarations
5026    /// displaced by parse recovery (issues #1537 and #3087). The parsed
5027    /// ancestors can stop short or extend past their true closes; see
5028    /// [`CppVisitor::recovered_namespace_scope`].
5029    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5030    /// Owned reparses waiting for the outer work loop. Partitioning another
5031    /// swallowed class in a tail must not grow the Rust call stack.
5032    pub partitioned_regions: Vec<CppPartitionedRegion>,
5033    /// The namespace forward declarations already folded out of each tree this
5034    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
5035    /// Empty until the first question, which the overwhelming majority of files
5036    /// never ask.
5037    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5038    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
5039    /// happen to the declaration set, innermost last. Empty outside a recovery
5040    /// reparse, which is almost always (#2787).
5041    pub recovery_captures: Vec<CppRecoveryCapture>,
5042    active_source_context: Option<CppActiveSourceContext>,
5043    /// Object-like field-list macros defined earlier in this source. Their
5044    /// replacements are parsed structurally and materialized under each
5045    /// invoking aggregate; no source-text expansion is used.
5046    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5047    /// Names whose active replacement is not a unique structured field list.
5048    /// A later `#undef` resets the ambiguity; another `#define` does not.
5049    pub ambiguous_object_macro_fields: HashSet<String>,
5050}
5051
5052/// One source-order event from an object-like field-list macro environment.
5053///
5054/// The event stream lets an include-closure consumer preserve preprocessor
5055/// invalidation without guessing which parsed header happened to be visited
5056/// first. A conservative consumer may permanently block a name after an
5057/// `undef` when conditional include order is not provable.
5058#[derive(Clone, Debug, PartialEq, Eq)]
5059pub enum ObjectMacroFieldEvent {
5060    Define {
5061        name: String,
5062        replacement: ObjectMacroReplacement,
5063        conditional: bool,
5064    },
5065    Undef {
5066        name: String,
5067        conditional: bool,
5068    },
5069}
5070
5071/// Collect object-like field-list macro events in source order. Conditional
5072/// branches remain visible in the event stream: callers that cannot prove
5073/// branch selection can invalidate names conservatively instead of inventing
5074/// a replacement from one branch.
5075pub fn collect_cpp_object_macro_field_events<'tree>(
5076    root: Node<'tree>,
5077    source: &str,
5078) -> Vec<ObjectMacroFieldEvent> {
5079    let mut events = Vec::new();
5080    let mut stack = vec![root];
5081    while let Some(node) = stack.pop() {
5082        if node.kind() == "preproc_def"
5083            && let Some(name) = extract_macro_name(node, source)
5084        {
5085            let replacement = object_macro_replacement_of(node, source);
5086            events.push(ObjectMacroFieldEvent::Define {
5087                name,
5088                replacement,
5089                conditional: inside_preprocessor_conditional(node),
5090            });
5091        } else if is_cpp_undef_directive(node, source)
5092            && let Some(argument) = node.child_by_field_name("argument")
5093        {
5094            events.push(ObjectMacroFieldEvent::Undef {
5095                name: node_text(argument, source).trim().to_string(),
5096                conditional: inside_preprocessor_conditional(node),
5097            });
5098        }
5099        let mut cursor = node.walk();
5100        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5101        stack.extend(children.into_iter().rev());
5102    }
5103    events
5104}
5105
5106fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5107    let mut current = node.parent();
5108    while let Some(parent) = current {
5109        if is_cpp_preprocessor_conditional(parent) {
5110            return true;
5111        }
5112        current = parent.parent();
5113    }
5114    false
5115}
5116
5117fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5118    node.kind() == "preproc_call"
5119        && node
5120            .child_by_field_name("directive")
5121            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5122}
5123
5124impl<'a, 'source> CppVisitor<'a, 'source> {
5125    fn apply_active_source_context(
5126        &self,
5127        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5128    ) {
5129        let Some(context) = self.active_source_context.as_ref() else {
5130            return;
5131        };
5132        fact.guard_requirements = fact
5133            .guard_requirements
5134            .as_ref()
5135            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5136            .zip(context.guards.as_ref())
5137            .and_then(|(local, outer)| {
5138                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5139            })
5140            .as_ref()
5141            .map(crate::source_context::cpp_guard_set_from_runtime);
5142        if fact.callable_guards.is_some() {
5143            fact.callable_guards = fact
5144                .callable_guards
5145                .as_ref()
5146                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5147                .zip(context.guards.as_ref())
5148                .and_then(|(local, outer)| {
5149                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5150                })
5151                .as_ref()
5152                .map(crate::source_context::cpp_guard_set_from_runtime);
5153        }
5154        fact.callable_guard_completion_byte = fact
5155            .callable_guard_completion_byte
5156            .zip(context.callable_guard_completion_byte)
5157            .map(|(local, outer)| local.max(outer));
5158        fact.conditional_family = fact.conditional_family.or(context.family);
5159        fact.exhaustive_conditional_family = fact
5160            .exhaustive_conditional_family
5161            .or(context.exhaustive_family);
5162        if let Some(alias) = fact.file_scope_alias.as_mut() {
5163            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5164                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5165                (Some(outer), None) => Some(outer.clone()),
5166                (None, local) => local,
5167            };
5168        }
5169        if !context.outer_lexical_scope.is_empty() {
5170            let mut lexical = context.outer_lexical_scope.clone();
5171            lexical.append(&mut fact.lexical_path);
5172            fact.lexical_path = lexical;
5173        }
5174    }
5175
5176    fn recovery_ancestry<'tree>(
5177        &self,
5178        root: Node<'tree>,
5179    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5180        crate::source_dependencies::RecoveryAncestry::new(
5181            root,
5182            self.source,
5183            self.source_dependencies,
5184        )
5185    }
5186
5187    fn capture_source_dependencies(
5188        &self,
5189        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5190        node: Node<'_>,
5191        unit: &CodeUnit,
5192    ) {
5193        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5194            self.source_dependencies.borrow_mut().register_primary(
5195                fact.declaration,
5196                node,
5197                unit.is_class(),
5198            );
5199        } else {
5200            self.source_dependencies
5201                .borrow()
5202                .apply_recovered(node, unit.is_class(), fact);
5203        }
5204    }
5205
5206    fn link_source_declaration(
5207        &mut self,
5208        ancestry: &ParentIndex<'_>,
5209        unit: &CodeUnit,
5210        node: Node<'_>,
5211    ) {
5212        let (declaration, family) = {
5213            let mut collector = self.source_collector.borrow_mut();
5214            let occurrence = if node.id() == self.primary_root_id
5215                || self.primary_ancestry.contains(node)
5216            {
5217                collector.intern_node(node)
5218            } else {
5219                // A region reparse owns distinct provenance even when its byte range
5220                // equals a primary node or a previously dropped recovery tree.
5221                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5222            };
5223            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5224                if self.primary_ancestry.contains(name) {
5225                    collector.intern_node(name)
5226                } else {
5227                    collector
5228                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5229                }
5230            });
5231            let declaration = collector.declare(occurrence, name);
5232            let family =
5233                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5234                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5235                    {
5236                        collector.intern_node(family)
5237                    } else {
5238                        collector.intern_embedded(
5239                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5240                        )
5241                    }
5242                });
5243            (declaration, family)
5244        };
5245        self.source_declarations.insert(declaration);
5246        if !self.source_properties.borrow().contains_key(&declaration) {
5247            let context = crate::source_context::capture_declaration_context(
5248                node,
5249                unit,
5250                self.source,
5251                &mut self.preprocessor_context_cache,
5252            );
5253            let (exhaustive_family, displaced_namespace_closing_brace) = {
5254                let mut collector = self.source_collector.borrow_mut();
5255                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5256                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5257                    {
5258                        collector.intern_node(family)
5259                    } else {
5260                        collector.intern_embedded(
5261                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5262                        )
5263                    }
5264                });
5265                let displaced_namespace_closing_brace =
5266                    context.displaced_namespace_closing_brace.map(|brace| {
5267                        if brace.id() == self.primary_root_id
5268                            || self.primary_ancestry.contains(brace)
5269                        {
5270                            collector.intern_node(brace)
5271                        } else {
5272                            collector.intern_embedded(
5273                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5274                            )
5275                        }
5276                    });
5277                (exhaustive_family, displaced_namespace_closing_brace)
5278            };
5279            let guard_requirements = context.guard_requirements;
5280            let callable_guards = context.callable_guards;
5281            let callable_activation = context.callable_activation;
5282            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5283            let flattened_macro_namespace = context.flattened_macro_namespace;
5284            self.source_properties
5285                .borrow_mut()
5286                .entry(declaration)
5287                .or_insert_with(|| {
5288                    let mut fact = crate::source_facts::capture_declaration(
5289                        declaration,
5290                        node,
5291                        unit,
5292                        self.source,
5293                        ancestry,
5294                    );
5295                    self.capture_source_dependencies(&mut fact, node, unit);
5296                    fact.occurrence_role =
5297                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5298                    fact.conditional_family = family;
5299                    fact.guard_requirements = guard_requirements;
5300                    fact.callable_guards = callable_guards;
5301                    fact.callable_activation = callable_activation;
5302                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5303                    fact.exhaustive_conditional_family = exhaustive_family;
5304                    fact.flattened_macro_namespace = flattened_macro_namespace;
5305                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5306                    self.apply_active_source_context(&mut fact);
5307                    fact
5308                });
5309        }
5310        let links = self.source_links.entry(unit.clone()).or_default();
5311        if !links.contains(&declaration) {
5312            links.push(declaration);
5313        }
5314    }
5315
5316    fn link_source_range(
5317        &mut self,
5318        ancestry: &ParentIndex<'_>,
5319        unit: &CodeUnit,
5320        range: Range,
5321        evidence: Option<Node<'_>>,
5322    ) {
5323        let declaration = {
5324            let mut collector = self.source_collector.borrow_mut();
5325            let occurrence =
5326                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5327            let name = evidence
5328                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5329                .map(|name| {
5330                    if self.primary_ancestry.contains(name) {
5331                        collector.intern_node(name)
5332                    } else {
5333                        collector.intern_embedded(
5334                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5335                        )
5336                    }
5337                });
5338            collector.declare(occurrence, name)
5339        };
5340        self.link_source_declaration_facts(
5341            unit,
5342            declaration,
5343            evidence.map(|node| (ancestry, node)),
5344        );
5345    }
5346
5347    /// [`Self::link_source_range`] with the name occurrence interned from the
5348    /// subspan that writes it instead of derived from an evidence node. A live
5349    /// evidence node still supplies the declaration's source facts.
5350    fn link_source_name_range(
5351        &mut self,
5352        unit: &CodeUnit,
5353        range: Range,
5354        name_range: Range,
5355        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5356    ) {
5357        let declaration = {
5358            let mut collector = self.source_collector.borrow_mut();
5359            let occurrence =
5360                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5361            let name =
5362                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5363            collector.declare(occurrence, Some(name))
5364        };
5365        self.link_source_declaration_facts(unit, declaration, evidence);
5366    }
5367
5368    /// Record `declaration` for `unit` and, when a live evidence node exists,
5369    /// the source facts it supplies: conditional family, guards, context and
5370    /// dependency names. Shared by both occurrence-naming paths so a name
5371    /// subspan never costs a declaration its facts (#3093).
5372    fn link_source_declaration_facts(
5373        &mut self,
5374        unit: &CodeUnit,
5375        declaration: SourceDeclarationId,
5376        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5377    ) {
5378        let family = evidence.and_then(|(_, node)| {
5379            let mut collector = self.source_collector.borrow_mut();
5380            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5381                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5382                    collector.intern_node(family)
5383                } else {
5384                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5385                        family,
5386                    ))
5387                }
5388            })
5389        });
5390        self.source_declarations.insert(declaration);
5391        if let Some((ancestry, node)) = evidence
5392            && !self.source_properties.borrow().contains_key(&declaration)
5393        {
5394            let context = crate::source_context::capture_declaration_context(
5395                node,
5396                unit,
5397                self.source,
5398                &mut self.preprocessor_context_cache,
5399            );
5400            let (exhaustive_family, displaced_namespace_closing_brace) = {
5401                let mut collector = self.source_collector.borrow_mut();
5402                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5403                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5404                    {
5405                        collector.intern_node(family)
5406                    } else {
5407                        collector.intern_embedded(
5408                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5409                        )
5410                    }
5411                });
5412                let displaced_namespace_closing_brace =
5413                    context.displaced_namespace_closing_brace.map(|brace| {
5414                        if brace.id() == self.primary_root_id
5415                            || self.primary_ancestry.contains(brace)
5416                        {
5417                            collector.intern_node(brace)
5418                        } else {
5419                            collector.intern_embedded(
5420                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5421                            )
5422                        }
5423                    });
5424                (exhaustive_family, displaced_namespace_closing_brace)
5425            };
5426            let guard_requirements = context.guard_requirements;
5427            let callable_guards = context.callable_guards;
5428            let callable_activation = context.callable_activation;
5429            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5430            let flattened_macro_namespace = context.flattened_macro_namespace;
5431            let mut fact = crate::source_facts::capture_declaration(
5432                declaration,
5433                node,
5434                unit,
5435                self.source,
5436                ancestry,
5437            );
5438            self.capture_source_dependencies(&mut fact, node, unit);
5439            fact.occurrence_role =
5440                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5441            fact.conditional_family = family;
5442            fact.guard_requirements = guard_requirements;
5443            fact.callable_guards = callable_guards;
5444            fact.callable_activation = callable_activation;
5445            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5446            fact.exhaustive_conditional_family = exhaustive_family;
5447            fact.flattened_macro_namespace = flattened_macro_namespace;
5448            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5449            self.apply_active_source_context(&mut fact);
5450            self.source_properties
5451                .borrow_mut()
5452                .insert(declaration, fact);
5453        }
5454        let links = self.source_links.entry(unit.clone()).or_default();
5455        if !links.contains(&declaration) {
5456            links.push(declaration);
5457        }
5458    }
5459
5460    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5461        let declaration = *self
5462            .source_links
5463            .get(&unit)
5464            .and_then(|links| links.last())
5465            .expect("signature metadata follows its source declaration");
5466        let metadata_ordinal = self
5467            .parsed
5468            .add_signature_with_metadata(unit.clone(), metadata);
5469        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5470            declaration,
5471            unit,
5472            metadata_ordinal,
5473        };
5474        if !self.parsed.source_declaration_metadata.contains(&link) {
5475            self.parsed.source_declaration_metadata.push(link);
5476        }
5477    }
5478
5479    /// Records `code_unit` with the answers the walk carries forward, then adds
5480    /// it to the parse product.
5481    ///
5482    /// Every declaration this walk publishes goes through this family, so the
5483    /// carried-forward answers see each one exactly once: the minted declaration
5484    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5485    fn add_declaration(
5486        &mut self,
5487        ancestry: &ParentIndex<'_>,
5488        code_unit: CodeUnit,
5489        node: Node<'_>,
5490        parent: Option<CodeUnit>,
5491        top_level: Option<CodeUnit>,
5492    ) {
5493        self.note_declaration(&code_unit);
5494        self.link_source_declaration(ancestry, &code_unit, node);
5495        let source = self.source;
5496        self.parsed
5497            .add_code_unit(code_unit, node, source, parent, top_level);
5498    }
5499
5500    /// Range-based form of [`Self::add_declaration`].
5501    fn add_declaration_with_range(
5502        &mut self,
5503        ancestry: &ParentIndex<'_>,
5504        code_unit: CodeUnit,
5505        range: Range,
5506        evidence: Option<Node<'_>>,
5507        parent: Option<CodeUnit>,
5508        top_level: Option<CodeUnit>,
5509    ) {
5510        self.note_declaration(&code_unit);
5511        self.link_source_range(ancestry, &code_unit, range, evidence);
5512        self.parsed
5513            .add_code_unit_with_range(code_unit, range, parent, top_level);
5514    }
5515
5516    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5517    /// occurrence is the recorded subspan rather than a token derived from an
5518    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5519    /// still supplies the declaration's source facts; an index-recovered
5520    /// declaration has none.
5521    fn add_declaration_with_name_range(
5522        &mut self,
5523        code_unit: CodeUnit,
5524        range: Range,
5525        name_range: Range,
5526        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5527        parent: Option<CodeUnit>,
5528        top_level: Option<CodeUnit>,
5529    ) {
5530        self.note_declaration(&code_unit);
5531        self.link_source_name_range(&code_unit, range, name_range, evidence);
5532        self.parsed
5533            .add_code_unit_with_range(code_unit, range, parent, top_level);
5534    }
5535
5536    /// Deferred-replacement form of [`Self::add_declaration`].
5537    fn replace_declaration_deferred(
5538        &mut self,
5539        ancestry: &ParentIndex<'_>,
5540        code_unit: CodeUnit,
5541        node: Node<'_>,
5542        parent: Option<CodeUnit>,
5543        top_level: Option<CodeUnit>,
5544    ) {
5545        self.note_replaced_declaration(&code_unit);
5546        self.link_source_declaration(ancestry, &code_unit, node);
5547        let source = self.source;
5548        self.parsed
5549            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5550    }
5551
5552    /// Range-based form of [`Self::replace_declaration_deferred`].
5553    fn replace_declaration_with_range_deferred(
5554        &mut self,
5555        ancestry: &ParentIndex<'_>,
5556        code_unit: CodeUnit,
5557        range: Range,
5558        evidence: Option<Node<'_>>,
5559        parent: Option<CodeUnit>,
5560        top_level: Option<CodeUnit>,
5561    ) {
5562        self.note_replaced_declaration(&code_unit);
5563        self.link_source_range(ancestry, &code_unit, range, evidence);
5564        self.parsed
5565            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5566    }
5567
5568    /// Notes one declaration about to enter the parse product.
5569    ///
5570    /// A declaration the product already holds is not a creation, so an open
5571    /// recovery capture ignores it -- which is the membership test the set
5572    /// difference it replaces performed. A creation inside a nested recovery
5573    /// belongs to the recoveries around it too, so every open capture takes it.
5574    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5575        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5576            for capture in &mut self.recovery_captures {
5577                if capture.removed_pre_existing.contains(code_unit) {
5578                    continue;
5579                }
5580                if capture.created_units.insert(code_unit.clone()) {
5581                    capture.created.push(code_unit.clone());
5582                }
5583            }
5584        }
5585    }
5586
5587    /// Notes one declaration about to replace an existing one.
5588    ///
5589    /// A deferred replacement of a declaration that already owns children
5590    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5591    /// their source mounts and recovery captures must track the removal.
5592    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5593        // Forward and definition occurrences remain real navigation alternatives.
5594        // Only identities removed with replaced children lose their mounts.
5595        for removed in self.declarations_a_replacement_removes(code_unit) {
5596            self.source_links.remove(&removed);
5597        }
5598        let removes_children = self.parsed.contains_declaration(code_unit)
5599            && self
5600                .parsed
5601                .children
5602                .get(code_unit)
5603                .is_some_and(|children| !children.is_empty());
5604        if removes_children && !self.recovery_captures.is_empty() {
5605            let removed = self.declarations_a_replacement_removes(code_unit);
5606            for capture in &mut self.recovery_captures {
5607                for unit in &removed {
5608                    // A declaration this capture watched being created is
5609                    // its own; one it did not is a declaration that was
5610                    // already there when the capture opened, so creating it
5611                    // again is a restoration and not a mint.
5612                    if !capture.created_units.contains(unit) {
5613                        capture.removed_pre_existing.insert(unit.clone());
5614                    }
5615                }
5616            }
5617        }
5618        self.note_declaration(code_unit);
5619    }
5620
5621    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5622    /// when `code_unit` is replaced: its children, transitively.
5623    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5624        let mut removed = Vec::new();
5625        let mut seen = HashSet::default();
5626        let mut pending: Vec<CodeUnit> = self
5627            .parsed
5628            .children
5629            .get(code_unit)
5630            .cloned()
5631            .unwrap_or_default();
5632        while let Some(unit) = pending.pop() {
5633            if !seen.insert(unit.clone()) {
5634                continue;
5635            }
5636            if let Some(children) = self.parsed.children.get(&unit) {
5637                pending.extend(children.iter().cloned());
5638            }
5639            removed.push(unit);
5640        }
5641        removed
5642    }
5643
5644    fn visit_recovered_function_like_export_class<'tree>(
5645        &mut self,
5646        node: Node<'tree>,
5647        recovered: RecoveredFunctionLikeExportClass,
5648        scope: &ScopeInfo,
5649        stack: &mut Vec<CppWork<'tree>>,
5650        ancestry: &ParentIndex<'tree>,
5651    ) {
5652        let RecoveredFunctionLikeExportClass {
5653            name,
5654            range,
5655            raw_supertypes,
5656            base_facts,
5657            member_outcome,
5658        } = recovered;
5659        let class_unit = self.visit_named_class_like_shape(
5660            node,
5661            name,
5662            None,
5663            true,
5664            Some(range),
5665            raw_supertypes,
5666            base_facts,
5667            scope,
5668            stack,
5669            ancestry,
5670        );
5671        self.parsed
5672            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5673                recovery: range,
5674                unit: class_unit.clone(),
5675            });
5676        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5677            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5678                tree.root_node(),
5679                class_unit.identifier(),
5680                self.source,
5681            )
5682        {
5683            self.visit_recovered_fragment_constructor(
5684                constructor_range,
5685                body,
5686                node,
5687                &class_unit,
5688                scope,
5689                ancestry,
5690            );
5691        }
5692        if let Some(outcome) = member_outcome {
5693            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5694        }
5695    }
5696
5697    fn visit_function_like_export_class_pair<'tree>(
5698        &mut self,
5699        node: Node<'tree>,
5700        scope: &ScopeInfo,
5701        stack: &mut Vec<CppWork<'tree>>,
5702        ancestry: &ParentIndex<'tree>,
5703    ) -> bool {
5704        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5705            return false;
5706        };
5707        let member_outcome = self
5708            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5709        // A malformed class body can escape into several following siblings
5710        // before the next export-macro class head appears. Inspect siblings in
5711        // order and stop at the first envelope that contains such a head. One
5712        // envelope can contain several following classes, all recovered in a
5713        // single bounded traversal.
5714        let mut displaced = node.next_named_sibling();
5715        while let Some(candidate) = displaced {
5716            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5717                break;
5718            }
5719            displaced = candidate.next_named_sibling();
5720        }
5721        // The adjacent initializer_list proves the class body envelope, but
5722        // its children are expression-shaped rather than declaration-shaped.
5723        // Visit only separately recovered members; ordinary callable
5724        // definitions remain available from their out-of-line declarations.
5725        let range = recovered.range;
5726        self.visit_recovered_function_like_export_class(
5727            node,
5728            RecoveredFunctionLikeExportClass {
5729                name: recovered.name,
5730                range,
5731                raw_supertypes: recovered.raw_supertypes,
5732                base_facts: recovered.base_facts,
5733                member_outcome,
5734            },
5735            scope,
5736            stack,
5737            ancestry,
5738        );
5739        self.consumed_fragment_regions
5740            .push((node.start_byte(), range.end_byte));
5741        true
5742    }
5743
5744    fn visit_embedded_function_like_export_classes<'tree>(
5745        &mut self,
5746        node: Node<'tree>,
5747        scope: &ScopeInfo,
5748        stack: &mut Vec<CppWork<'tree>>,
5749        ancestry: &ParentIndex<'tree>,
5750    ) -> bool {
5751        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5752        let found = !recovered_classes.is_empty();
5753        for recovered in recovered_classes {
5754            let member_outcome = self.reparse_fragmented_export_class_members(
5755                &recovered.fragmented_body,
5756                &recovered.name,
5757            );
5758            self.visit_recovered_function_like_export_class(
5759                node,
5760                RecoveredFunctionLikeExportClass {
5761                    name: recovered.name,
5762                    range: recovered.range,
5763                    raw_supertypes: Some(recovered.raw_supertypes),
5764                    base_facts: Some(recovered.base_facts),
5765                    member_outcome,
5766                },
5767                scope,
5768                stack,
5769                ancestry,
5770            );
5771        }
5772        found
5773    }
5774
5775    /// Walk `node`'s container, answering every ancestor question from
5776    /// `ancestry`.
5777    ///
5778    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5779    /// because one tree can be walked more than once -- a header's C and C++
5780    /// readings are the same tree under different tag semantics -- and the
5781    /// parent relation is a property of the tree, not of the reading.
5782    #[allow(clippy::too_many_arguments)]
5783    pub fn visit_container<'tree>(
5784        &mut self,
5785        node: Node<'tree>,
5786        ancestry: &ParentIndex<'tree>,
5787        package_name: &str,
5788        module: Option<CodeUnit>,
5789        class_unit: Option<CodeUnit>,
5790        template_signature: Option<String>,
5791        visible_using_namespaces: Vec<String>,
5792        visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5793    ) {
5794        let scope = ScopeInfo {
5795            package_name: package_name.to_string(),
5796            module,
5797            class_unit,
5798            template_signature,
5799            template_metadata: None,
5800            declarations_are_fields: false,
5801            recovered_specialization_member_scope: false,
5802            visible_using_namespaces,
5803            local_block: None,
5804            visible_using_declarations,
5805        };
5806        // The work loop dispatches a container's children, never the container
5807        // itself, so an `ERROR` container that is the seed does not reach the
5808        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5809        // breaks the parse leaves the whole translation unit as that seed
5810        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5811        // macro-error-class recovery reads.
5812        if node.is_error() {
5813            self.visit_object_macro_error_classes(node, &scope);
5814        }
5815        self.run_container_work(node, scope, ancestry);
5816        self.drain_partitioned_regions();
5817    }
5818
5819    /// Drain reparsed containers after their primary walk has indexed them.
5820    /// Recovered nodes retain their own tree and source context while they are
5821    /// visited; only then are their byte ranges marked consumed so a shared
5822    /// primary event stream does not visit the same declarations twice.
5823    fn drain_partitioned_regions(&mut self) {
5824        let mut consumed = Vec::new();
5825        while let Some(CppPartitionedRegion {
5826            tree,
5827            range,
5828            scope,
5829            recovery,
5830            source_context,
5831        }) = self.partitioned_regions.pop()
5832        {
5833            let root = tree.root_node();
5834            let container = root
5835                .descendant_for_byte_range(range.start, range.end)
5836                .expect("the queued container belongs to this tree");
5837            assert_eq!(container.byte_range(), range);
5838            let ancestry = self.recovery_ancestry(root);
5839            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5840                visitor.run_container_work(container, scope, &ancestry)
5841            });
5842            // A recovered walk may enqueue another partition inside this
5843            // region. Delay the outer claim until all nested regions have
5844            // drained, or their nodes would be skipped as already consumed.
5845            consumed.push((range.start, range.end));
5846        }
5847        self.consumed_fragment_regions.extend(consumed);
5848    }
5849
5850    /// Whether a work node lies entirely inside a byte region consumed by a
5851    /// fragmented export-class recovery (#938); such nodes were already indexed
5852    /// as members of the recovered class by the region reparse.
5853    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5854        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5855    }
5856
5857    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5858    /// candidate a recovery is about to reparse but has not yet turned into a
5859    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5860    /// prior structured recovery.
5861    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5862        self.consumed_fragment_regions
5863            .iter()
5864            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5865    }
5866
5867    /// Drive the container work loop from an explicit seed scope to completion. The
5868    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5869    /// stays alive for the whole traversal.
5870    ///
5871    /// Every ancestor question this walk asks is answered from `ancestry`, which
5872    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5873    /// node's position in the tree, which made a generated header with thousands
5874    /// of top-level declarations quadratic (#2361). The index is the caller's
5875    /// because it outlives any one walk: the file's tree is walked twice when a
5876    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5877    /// builds its own index for its own tree.
5878    fn run_container_work<'tree>(
5879        &mut self,
5880        node: Node<'tree>,
5881        scope: ScopeInfo,
5882        ancestry: &ParentIndex<'tree>,
5883    ) {
5884        self.drain_cpp_work(
5885            vec![CppWork::Container(CppContainer { node, scope })],
5886            ancestry,
5887        );
5888    }
5889
5890    /// The work loop itself, from whatever seed the caller built.
5891    ///
5892    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5893    /// that must walk only part of a reparsed tree seeds it with the sibling
5894    /// range it may walk instead, which keeps the `using namespace X;` scope
5895    /// accumulation `advance_cpp_siblings` performs.
5896    fn drain_cpp_work<'tree>(
5897        &mut self,
5898        mut stack: Vec<CppWork<'tree>>,
5899        ancestry: &ParentIndex<'tree>,
5900    ) {
5901        while let Some(work) = stack.pop() {
5902            match work {
5903                CppWork::Container(container) => {
5904                    push_cpp_container_work(container.node, container.scope, &mut stack);
5905                }
5906                CppWork::Siblings(siblings) => {
5907                    advance_cpp_siblings(siblings, self.source, &mut stack);
5908                }
5909                CppWork::Node(work) => {
5910                    if self.node_is_inside_consumed_fragment(work.node) {
5911                        continue;
5912                    }
5913                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5914                }
5915            }
5916        }
5917    }
5918
5919    /// Drain nodes selected by a bounded recovery walk without filtering them
5920    /// against consumed regions from the primary tree.
5921    fn drain_cpp_recovery_work<'tree>(
5922        &mut self,
5923        mut stack: Vec<CppWork<'tree>>,
5924        ancestry: &ParentIndex<'tree>,
5925    ) {
5926        while let Some(work) = stack.pop() {
5927            match work {
5928                CppWork::Container(container) => {
5929                    push_cpp_container_work(container.node, container.scope, &mut stack);
5930                }
5931                CppWork::Siblings(siblings) => {
5932                    advance_cpp_siblings(siblings, self.source, &mut stack);
5933                }
5934                CppWork::Node(work) => {
5935                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
5936                }
5937            }
5938        }
5939    }
5940
5941    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
5942    /// it only when the entire region is member-shaped. This validation must happen
5943    /// before registering the recovered class because a rejected speculative range
5944    /// must not leak into the ordinary recovery path.
5945    fn reparse_fragmented_export_class_members(
5946        &self,
5947        fragmented: &FragmentedExportBody,
5948        class_name: &str,
5949    ) -> Option<FragmentedExportMembers> {
5950        if fragmented.reparse_start >= fragmented.reparse_end {
5951            return None;
5952        }
5953        let tree = cpp_reparse_fragmented_class_body(
5954            self.source,
5955            fragmented.reparse_start,
5956            fragmented.reparse_end,
5957        )?;
5958        if cpp_reparsed_members_are_indexable_for_class(
5959            tree.root_node(),
5960            self.source,
5961            Some(class_name),
5962        ) {
5963            return Some(FragmentedExportMembers::Complete(tree));
5964        }
5965        let has_conditional_constructor = {
5966            let root = tree.root_node();
5967            let mut cursor = root.walk();
5968            root.named_children(&mut cursor).any(|child| {
5969                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
5970            })
5971        };
5972        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
5973    }
5974
5975    /// Index an already validated fragmented body as members of `class_unit`. The
5976    /// region reparse keeps each member's exact original byte and line positions.
5977    fn visit_fragmented_export_class_members(
5978        &mut self,
5979        origin: Node<'_>,
5980        outcome: FragmentedExportMembers,
5981        class_unit: CodeUnit,
5982        scope: &ScopeInfo,
5983    ) -> bool {
5984        self.with_recovery_source_context(origin, |visitor| {
5985            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
5986        })
5987    }
5988
5989    fn visit_fragmented_export_class_members_in_context(
5990        &mut self,
5991        outcome: FragmentedExportMembers,
5992        class_unit: CodeUnit,
5993        scope: &ScopeInfo,
5994    ) -> bool {
5995        let (tree, complete) = match outcome {
5996            FragmentedExportMembers::Complete(tree) => (tree, true),
5997            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
5998        };
5999        let root = tree.root_node();
6000        let class_name = class_unit.identifier().to_string();
6001        let member_scope = ScopeInfo {
6002            // A recovered export-macro class may borrow its namespace from an
6003            // earlier forward declaration even when the malformed node itself
6004            // sits at file scope. Use the recovered class identity as the
6005            // authoritative package for reparsed members as well.
6006            package_name: class_unit.package_name().to_string(),
6007            module: scope.module.clone(),
6008            class_unit: Some(class_unit),
6009            template_signature: scope.template_signature.clone(),
6010            template_metadata: None,
6011            declarations_are_fields: true,
6012            recovered_specialization_member_scope: false,
6013            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6014            local_block: None,
6015            visible_using_declarations: scope.visible_using_declarations.clone(),
6016        };
6017        let reparsed_ancestry = self.recovery_ancestry(root);
6018        if !complete {
6019            // A conditional beginning immediately after an access label can
6020            // fragment one constructor declaration while leaving the rest of
6021            // the class body as unsafe statement soup. Recover only that
6022            // structurally proven constructor and leave the outer-tree
6023            // siblings unconsumed for their ordinary walk.
6024            let mut cursor = root.walk();
6025            let constructors = root
6026                .named_children(&mut cursor)
6027                .filter_map(|child| {
6028                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6029                })
6030                .collect::<Vec<_>>();
6031            // The reparsed region is its own tree, so this drain walks it with
6032            // its own parent index.
6033            for constructor in constructors {
6034                let mut stack = Vec::new();
6035                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6036                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6037            }
6038            return false;
6039        }
6040        let mut cursor = root.walk();
6041        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6042        for member in members.iter().copied().filter(|member| {
6043            cpp_reparsed_defaulted_constructor_is_indexable(
6044                *member,
6045                &members,
6046                Some(class_name.as_str()),
6047                self.source,
6048            )
6049        }) {
6050            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6051        }
6052        for member in members.iter().copied().filter(|member| {
6053            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6054        }) {
6055            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6056        }
6057        // The reparsed region is its own tree, so this walk indexes it itself.
6058        self.run_container_work(root, member_scope, &reparsed_ancestry);
6059        true
6060    }
6061
6062    fn visit_recovered_fragment_constructor<'tree>(
6063        &mut self,
6064        range: std::ops::Range<usize>,
6065        constructor_body: Node<'tree>,
6066        class_declaration: Node<'tree>,
6067        class_unit: &CodeUnit,
6068        scope: &ScopeInfo,
6069        ancestry: &ParentIndex<'tree>,
6070    ) {
6071        self.with_recovery_source_context(class_declaration, |visitor| {
6072            visitor.visit_recovered_fragment_constructor_in_context(
6073                range,
6074                constructor_body,
6075                class_declaration,
6076                class_unit,
6077                scope,
6078                ancestry,
6079            );
6080        });
6081    }
6082
6083    fn visit_recovered_fragment_constructor_in_context<'tree>(
6084        &mut self,
6085        range: std::ops::Range<usize>,
6086        constructor_body: Node<'tree>,
6087        class_declaration: Node<'tree>,
6088        class_unit: &CodeUnit,
6089        scope: &ScopeInfo,
6090        ancestry: &ParentIndex<'tree>,
6091    ) {
6092        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6093            return;
6094        };
6095        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6096            tree.root_node(),
6097            range.start,
6098            class_unit.identifier(),
6099            self.source,
6100        ) else {
6101            return;
6102        };
6103        let member_scope = ScopeInfo {
6104            package_name: class_unit.package_name().to_string(),
6105            module: scope.module.clone(),
6106            class_unit: Some(class_unit.clone()),
6107            template_signature: scope.template_signature.clone(),
6108            template_metadata: None,
6109            declarations_are_fields: true,
6110            recovered_specialization_member_scope: false,
6111            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6112            local_block: None,
6113            visible_using_declarations: scope.visible_using_declarations.clone(),
6114        };
6115        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6116        else {
6117            return;
6118        };
6119        debug_assert_eq!(function.name, class_unit.identifier());
6120        let code_unit = function.code_unit(self.file.clone());
6121        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6122        self.add_declaration_with_range(
6123            &recovered_ancestry,
6124            code_unit.clone(),
6125            Range {
6126                start_byte: function_declarator.start_byte(),
6127                end_byte: constructor_body.end_byte(),
6128                start_line: function_declarator.start_position().row + 1,
6129                end_line: constructor_body.end_position().row + 1,
6130            },
6131            Some(function_declarator),
6132            None,
6133            None,
6134        );
6135        let declaration = *self.source_links[&code_unit]
6136            .last()
6137            .expect("recovered constructor source");
6138        {
6139            let mut properties = self.source_properties.borrow_mut();
6140            let fact = properties
6141                .get_mut(&declaration)
6142                .expect("recovered constructor source fact");
6143            fact.occurrence_role =
6144                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6145            fact.callable_is_constructor = true;
6146        }
6147        self.add_signature_with_metadata(
6148            code_unit.clone(),
6149            cpp_signature_metadata(
6150                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6151                function_declarator,
6152                cpp_callable_linkage(class_declaration, self.source, ancestry),
6153                self.source,
6154                &recovered_ancestry,
6155            )
6156            .with_declaration_only(false),
6157        );
6158        self.parsed.add_child(class_unit.clone(), code_unit);
6159    }
6160
6161    fn visit_recovered_fragment_prefix_members<'tree>(
6162        &mut self,
6163        origin: Node<'_>,
6164        root: Node<'tree>,
6165        constructor_start: usize,
6166        class_unit: &CodeUnit,
6167        scope: &ScopeInfo,
6168        ancestry: &ParentIndex<'tree>,
6169    ) {
6170        self.with_recovery_source_context(origin, |visitor| {
6171            visitor.visit_recovered_fragment_prefix_members_in_context(
6172                root,
6173                constructor_start,
6174                class_unit,
6175                scope,
6176                ancestry,
6177            );
6178        });
6179    }
6180
6181    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6182        &mut self,
6183        root: Node<'tree>,
6184        constructor_start: usize,
6185        class_unit: &CodeUnit,
6186        scope: &ScopeInfo,
6187        ancestry: &ParentIndex<'tree>,
6188    ) {
6189        let member_scope = ScopeInfo {
6190            package_name: class_unit.package_name().to_string(),
6191            module: scope.module.clone(),
6192            class_unit: Some(class_unit.clone()),
6193            template_signature: scope.template_signature.clone(),
6194            template_metadata: None,
6195            declarations_are_fields: true,
6196            recovered_specialization_member_scope: false,
6197            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6198            local_block: None,
6199            visible_using_declarations: scope.visible_using_declarations.clone(),
6200        };
6201        let mut root_cursor = root.walk();
6202        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6203        let mut stack = vec![root];
6204        while let Some(current) = stack.pop() {
6205            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6206                continue;
6207            }
6208            if current
6209                .parent()
6210                .is_some_and(|parent| same_node(parent, root))
6211                && cpp_reparsed_defaulted_constructor_is_indexable(
6212                    current,
6213                    &members,
6214                    Some(class_unit.identifier()),
6215                    self.source,
6216                )
6217            {
6218                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6219                continue;
6220            }
6221            if current.end_byte() <= constructor_start
6222                && current.kind() != "translation_unit"
6223                && current.kind() != "labeled_statement"
6224                && current.kind() != "ERROR"
6225            {
6226                let mut work_stack = Vec::new();
6227                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6228                self.drain_cpp_recovery_work(work_stack, ancestry);
6229                continue;
6230            }
6231            if matches!(
6232                current.kind(),
6233                "translation_unit" | "labeled_statement" | "ERROR"
6234            ) {
6235                let mut cursor = current.walk();
6236                stack.extend(current.named_children(&mut cursor));
6237            }
6238        }
6239    }
6240
6241    fn visit_recovered_defaulted_constructor(
6242        &mut self,
6243        statement: Node<'_>,
6244        scope: &ScopeInfo,
6245        ancestry: &ParentIndex<'_>,
6246    ) {
6247        let Some(parent) = scope.class_unit.clone() else {
6248            return;
6249        };
6250        let Some(assignment) = statement.named_child(0) else {
6251            return;
6252        };
6253        let Some(call) = assignment.child_by_field_name("left") else {
6254            return;
6255        };
6256        let Some(function_name) = call.child_by_field_name("function") else {
6257            return;
6258        };
6259        let Some(arguments) = call.child_by_field_name("arguments") else {
6260            return;
6261        };
6262        if function_name.kind() != "identifier"
6263            || node_text(function_name, self.source) != parent.identifier()
6264            || arguments.kind() != "argument_list"
6265            || arguments.named_child_count() != 0
6266        {
6267            return;
6268        }
6269        let function = FunctionInfo {
6270            package_name: scope.package_name.clone(),
6271            owner: Some(CppMemberOwner::Unit(parent.clone())),
6272            name: parent.identifier().to_string(),
6273            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6274        };
6275        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6276        if self.parsed.contains_declaration(&code_unit) {
6277            return;
6278        }
6279        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6280        let metadata = SignatureMetadata::new(
6281            normalize_cpp_whitespace(node_text(statement, self.source)),
6282            Vec::new(),
6283        )
6284        .with_declaration_only(true)
6285        .with_callable_arity(CallableArity::exact(0))
6286        .with_overload_discriminator(Some(
6287            brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6288        ))
6289        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6290        // The reparse proves class ownership, but carries no original class
6291        // access ladder. Preserve unknown access rather than guessing it.
6292        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6293        self.add_signature_with_metadata(code_unit.clone(), metadata);
6294        if let Some(declaration) = self
6295            .source_links
6296            .get(&code_unit)
6297            .and_then(|links| links.last())
6298            .copied()
6299        {
6300            let mut properties = self.source_properties.borrow_mut();
6301            let fact = properties
6302                .get_mut(&declaration)
6303                .expect("recovered defaulted constructor source fact");
6304            fact.occurrence_role =
6305                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6306            fact.callable_is_constructor = true;
6307        }
6308        self.parsed.add_child(parent, code_unit);
6309    }
6310
6311    /// Recover a constructor declaration a statement-context class-body
6312    /// reparse read as a call or parenthesized variable declarator. The
6313    /// statement is validated and its parameters read from a synthetic class
6314    /// context, where the constructor declaration parses as a real function
6315    /// declarator; the declaration itself is minted from the original
6316    /// statement node, so every range keeps its original byte positions.
6317    fn visit_bare_constructor_expression<'tree>(
6318        &mut self,
6319        statement: Node<'tree>,
6320        scope: &ScopeInfo,
6321        ancestry: &ParentIndex<'tree>,
6322    ) -> bool {
6323        let Some(parent) = scope.class_unit.clone() else {
6324            return false;
6325        };
6326        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6327        let region = &self.source[statement.start_byte()..statement.end_byte()];
6328        let parsed = crate::graph::syntax::parse_sentinel(
6329            PREFIX,
6330            crate::graph::syntax::SentinelInput::region(region),
6331            " };",
6332        );
6333        let (synthetic, tree) = (parsed.source, parsed.tree);
6334        let root = tree.root_node();
6335        if root.has_error() {
6336            return false;
6337        }
6338        let mut declarator = None;
6339        let mut stack = vec![root];
6340        while let Some(current) = stack.pop() {
6341            if current.kind() == "function_declarator" {
6342                if declarator.is_some() {
6343                    return false;
6344                }
6345                declarator = Some(current);
6346                continue;
6347            }
6348            let mut cursor = current.walk();
6349            stack.extend(current.named_children(&mut cursor));
6350        }
6351        let Some(declarator) = declarator else {
6352            return false;
6353        };
6354        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6355            return false;
6356        };
6357        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6358        let mut parameter_labels = Vec::new();
6359        let mut parameter_cursor = parameters_node.walk();
6360        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6361            if matches!(
6362                parameter.kind(),
6363                "parameter_declaration" | "optional_parameter_declaration"
6364            ) && let Some(name) = parameter
6365                .child_by_field_name("declarator")
6366                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6367            {
6368                parameter_labels.push(name);
6369            }
6370        }
6371        let arity = parameter_labels.len();
6372        let function = FunctionInfo {
6373            package_name: scope.package_name.clone(),
6374            owner: Some(CppMemberOwner::Unit(parent.clone())),
6375            name: parent.identifier().to_string(),
6376            signature,
6377        };
6378        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6379        if self.parsed.contains_declaration(&code_unit) {
6380            return true;
6381        }
6382        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6383        let metadata = SignatureMetadata::with_parameter_labels(
6384            normalize_cpp_whitespace(node_text(statement, self.source)),
6385            parameter_labels,
6386        )
6387        .with_declaration_only(true)
6388        .with_callable_arity(CallableArity::exact(arity))
6389        .with_overload_discriminator(Some(cpp_overload_discriminator(declarator, &synthetic)))
6390        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6391        // The reparse proves class ownership, but carries no original
6392        // class access ladder. Preserve unknown access rather than
6393        // guessing it.
6394        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6395        self.add_signature_with_metadata(code_unit.clone(), metadata);
6396        if let Some(declaration) = self
6397            .source_links
6398            .get(&code_unit)
6399            .and_then(|links| links.last())
6400            .copied()
6401        {
6402            let mut properties = self.source_properties.borrow_mut();
6403            let fact = properties
6404                .get_mut(&declaration)
6405                .expect("recovered constructor source fact");
6406            fact.occurrence_role =
6407                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6408            fact.callable_is_constructor = true;
6409        }
6410        self.parsed.add_child(parent, code_unit);
6411        true
6412    }
6413
6414    /// Capture ancestors only when a proven recovery enters another tree. The
6415    /// ordinary declaration driver never performs this additional ancestor walk.
6416    fn with_recovery_source_context<T>(
6417        &mut self,
6418        node: Node<'_>,
6419        walk: impl FnOnce(&mut Self) -> T,
6420    ) -> T {
6421        self.with_recovery_source_context_at(node, node, walk)
6422    }
6423
6424    /// [`Self::with_recovery_source_context`] for a recovery whose content
6425    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6426    /// from `node`, the preprocessor facts from the anchor. The two differ when
6427    /// the malformed node swallowed a branch directive that precedes the
6428    /// recovered content.
6429    fn with_recovery_source_context_at<T>(
6430        &mut self,
6431        node: Node<'_>,
6432        preprocessor_anchor: Node<'_>,
6433        walk: impl FnOnce(&mut Self) -> T,
6434    ) -> T {
6435        use crate::graph::resolver::{
6436            enclosing_namespace_context, merge_preprocessor_guards,
6437            preprocessor_conditional_family_for_declaration_with_cache,
6438            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6439        };
6440        let previous = self.active_source_context.take();
6441        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6442        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6443        let local_guards = preprocessor_guard_environment_with_cache(
6444            preprocessor_anchor,
6445            self.source,
6446            &mut self.preprocessor_context_cache,
6447        );
6448        let guards = match inherited {
6449            Some(outer) => outer
6450                .guards
6451                .as_ref()
6452                .zip(local_guards.as_ref())
6453                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6454            None => local_guards,
6455        };
6456        let local_namespace = enclosing_namespace_context(node, self.source);
6457        let namespace = match (
6458            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6459            local_namespace,
6460        ) {
6461            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6462            (Some(outer), None) => Some(outer.clone()),
6463            (None, local) => local,
6464        };
6465        let outer_lexical_scope =
6466            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6467        let mut local_owners = Vec::new();
6468        let mut parent = node.parent();
6469        while let Some(ancestor) = parent {
6470            if matches!(
6471                ancestor.kind(),
6472                "namespace_definition"
6473                    | "class_specifier"
6474                    | "struct_specifier"
6475                    | "union_specifier"
6476                    | "enum_specifier"
6477            ) && let Some(name) = ancestor.child_by_field_name("name")
6478            {
6479                local_owners.push(node_text(name, self.source).to_owned());
6480            }
6481            parent = ancestor.parent();
6482        }
6483        local_owners.reverse();
6484        let mut child_lexical_scope = outer_lexical_scope.clone();
6485        child_lexical_scope.extend(local_owners);
6486        if matches!(
6487            node.kind(),
6488            "namespace_definition"
6489                | "class_specifier"
6490                | "struct_specifier"
6491                | "union_specifier"
6492                | "enum_specifier"
6493        ) && let Some(name) = node.child_by_field_name("name")
6494        {
6495            child_lexical_scope.push(node_text(name, self.source).to_owned());
6496        }
6497        let child_namespace = if node.kind() == "namespace_definition" {
6498            node.child_by_field_name("name")
6499                .map(|name| {
6500                    let local = node_text(name, self.source);
6501                    namespace
6502                        .as_ref()
6503                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6504                })
6505                .or_else(|| namespace.clone())
6506        } else {
6507            namespace.clone()
6508        };
6509        let intern = |family: Node<'_>| {
6510            let mut collector = self.source_collector.borrow_mut();
6511            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6512                collector.intern_node(family)
6513            } else {
6514                collector
6515                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6516            }
6517        };
6518        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6519            .map(intern)
6520            .or_else(|| inherited.and_then(|outer| outer.family));
6521        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6522            preprocessor_anchor,
6523            &mut self.preprocessor_context_cache,
6524        )
6525        .map(intern)
6526        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6527        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6528            preprocessor_anchor,
6529            self.source,
6530            &mut self.preprocessor_context_cache,
6531        );
6532        let callable_guard_completion_byte = match inherited {
6533            Some(outer) => local_completion
6534                .zip(outer.callable_guard_completion_byte)
6535                .map(|(local, outer)| local.max(outer)),
6536            None => local_completion,
6537        };
6538        self.active_source_context = Some(CppActiveSourceContext {
6539            guards,
6540            namespace: child_namespace.clone(),
6541            child_namespace,
6542            outer_lexical_scope: child_lexical_scope.clone(),
6543            child_lexical_scope,
6544            family,
6545            exhaustive_family,
6546            callable_guard_completion_byte,
6547        });
6548        let result = walk(self);
6549        self.active_source_context = previous;
6550        result
6551    }
6552
6553    fn visit_node<'tree>(
6554        &mut self,
6555        node: Node<'tree>,
6556        scope: &ScopeInfo,
6557        stack: &mut Vec<CppWork<'tree>>,
6558        ancestry: &ParentIndex<'tree>,
6559    ) {
6560        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6561            self.visit_node(node, &recovered_scope, stack, ancestry);
6562            return;
6563        }
6564        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6565            self.visit_node(node, &recovered_scope, stack, ancestry);
6566            return;
6567        }
6568        if scope.class_unit.is_some()
6569            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6570        {
6571            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6572            return;
6573        }
6574        // Fragmented-class recovery below may consume a malformed function
6575        // envelope before the ordinary kind dispatch runs. Recover any
6576        // export-macro class embedded in that envelope first; the strict class
6577        // head/base/body predicate is independent of which later recovery owns
6578        // the surrounding parser fragment.
6579        if node.kind() == "function_definition" && node.has_error() {
6580            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6581        }
6582        if let Some(FragmentedClassRecovery {
6583            declaration_node: class_node,
6584            name,
6585            raw_supertypes,
6586            body: fragmented,
6587        }) = fragmented_class_body(node, self.source)
6588        {
6589            let displaced_namespace_items =
6590                displaced_fragment_namespace_geometry(node, self.source)
6591                    .map(|boundary| boundary.namespace_items)
6592                    .unwrap_or_default();
6593            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6594            let mut class_stack = Vec::new();
6595            // When the full body cannot be safely reparsed, the original class
6596            // node still proves ownership for its parser-visible prefix.
6597            let parser_visible_body =
6598                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6599                    .then(|| cpp_body_node(class_node))
6600                    .flatten();
6601            let class_unit = self.visit_named_class_like_shape(
6602                class_node,
6603                name,
6604                parser_visible_body,
6605                true,
6606                Some(fragmented.class_range),
6607                Some(raw_supertypes),
6608                Some(extract_cpp_base_facts(class_node, self.source)),
6609                scope,
6610                &mut class_stack,
6611                ancestry,
6612            );
6613            let member_scope = ScopeInfo {
6614                package_name: class_unit.package_name().to_string(),
6615                module: scope.module.clone(),
6616                class_unit: Some(class_unit.clone()),
6617                template_signature: scope.template_signature.clone(),
6618                template_metadata: None,
6619                declarations_are_fields: true,
6620                recovered_specialization_member_scope: false,
6621                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6622                local_block: None,
6623                visible_using_declarations: scope.visible_using_declarations.clone(),
6624            };
6625            let complete = outcome.is_some_and(|outcome| {
6626                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6627            });
6628            if complete {
6629                self.consumed_fragment_regions
6630                    .push((node.start_byte(), fragmented.class_range.end_byte));
6631            } else {
6632                // A macro-constrained member can make the full body reparse
6633                // unsafe while tree-sitter still exposes later class members
6634                // as bounded siblings up to the displaced `}`/`;`. Keep the
6635                // structurally proven class/base declaration and re-own those
6636                // sibling nodes under it. They retain their original parser
6637                // nodes and exact ranges; the close boundary comes solely from
6638                // `fragmented_class_body`.
6639                // Template wrappers put the escaped members beside the
6640                // template rather than beside its malformed declaration.
6641                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6642                    if candidate.start_byte() >= fragmented.reparse_end {
6643                        break;
6644                    }
6645                    if cpp_fragment_sibling_is_class_member(
6646                        candidate,
6647                        fragmented.reparse_end,
6648                        self.source,
6649                    ) {
6650                        self.recovered_class_sibling_scopes
6651                            .insert(candidate.id(), member_scope.clone());
6652                    }
6653                }
6654                // The macro-declaration grammar keeps the class head and its
6655                // parser-visible prefix members inside the fragmented
6656                // container itself, where there is no separate body node for
6657                // `visit_named_class_like_shape` to seed. Walk the container
6658                // with the member scope so those prefix members are owned by
6659                // the class; the head's bare tokens mint nothing on their own.
6660                if class_node.kind() == "ERROR" && class_node.has_error() {
6661                    stack.push(CppWork::Container(CppContainer {
6662                        node: class_node,
6663                        scope: member_scope,
6664                    }));
6665                }
6666            }
6667            for item in displaced_namespace_items {
6668                self.recovered_class_sibling_scopes
6669                    .insert(item.id(), scope.clone());
6670            }
6671            stack.extend(class_stack);
6672            return;
6673        }
6674        if self.visit_folded_aggregate(node, scope) {
6675            return;
6676        }
6677        match node.kind() {
6678            "template_declaration" => {
6679                if let Some(recovered) =
6680                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6681                {
6682                    let mut template_scope = scope.clone();
6683                    template_scope.template_signature =
6684                        cpp_template_signature(node, recovered.declaration_node, self.source);
6685                    template_scope.template_metadata =
6686                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6687                    let raw_supertypes =
6688                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6689                    let mut class_stack = Vec::new();
6690                    let class_unit = self.visit_named_class_like_shape(
6691                        recovered.class_node,
6692                        recovered.name,
6693                        Some(recovered.body),
6694                        true,
6695                        Some(recovered.range),
6696                        raw_supertypes,
6697                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6698                        &template_scope,
6699                        &mut class_stack,
6700                        ancestry,
6701                    );
6702                    self.parsed.record_materialization(
6703                        MaterializationRecord::RecoveredDeclaration {
6704                            recovery: recovered.range,
6705                            unit: class_unit.clone(),
6706                        },
6707                    );
6708                    let member_scope = ScopeInfo {
6709                        package_name: template_scope.package_name.clone(),
6710                        module: template_scope.module.clone(),
6711                        class_unit: Some(class_unit.clone()),
6712                        template_signature: template_scope.template_signature.clone(),
6713                        template_metadata: None,
6714                        declarations_are_fields: true,
6715                        recovered_specialization_member_scope: recovered
6716                            .class_node
6717                            .child_by_field_name("name")
6718                            .is_some_and(|name| name.kind() == "template_type"),
6719                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6720                        local_block: None,
6721                        visible_using_declarations: template_scope
6722                            .visible_using_declarations
6723                            .clone(),
6724                    };
6725                    for tail_member in recovered.tail_members.into_iter().rev() {
6726                        stack.push(CppWork::Node(CppNodeWork {
6727                            node: tail_member,
6728                            scope: member_scope.clone(),
6729                        }));
6730                    }
6731                    stack.extend(class_stack);
6732                    for sibling in recovered.member_siblings {
6733                        self.recovered_class_sibling_scopes
6734                            .insert(sibling.id(), member_scope.clone());
6735                    }
6736                    return;
6737                }
6738                for index in (0..node.named_child_count()).rev() {
6739                    let Some(child) = node.named_child(index) else {
6740                        continue;
6741                    };
6742                    if matches!(
6743                        child.kind(),
6744                        "class_specifier"
6745                            | "struct_specifier"
6746                            | "union_specifier"
6747                            | "enum_specifier"
6748                            | "function_definition"
6749                            | "declaration"
6750                            | "field_declaration"
6751                            | "alias_declaration"
6752                            | "namespace_definition"
6753                            // `template <class T> template <class U> void
6754                            // Box<T>::take(U) {}`: an out-of-line member
6755                            // template nests one head inside another. The
6756                            // inner head is the member's own and replaces the
6757                            // signature head when the nested node is visited.
6758                            | "template_declaration"
6759                    ) {
6760                        let mut template_scope = scope.clone();
6761                        template_scope.template_signature =
6762                            cpp_template_signature(node, child, self.source);
6763                        template_scope.template_metadata =
6764                            cpp_template_metadata(node, child, self.source, ancestry);
6765                        if let Some(recovered) = recover_fragmented_partial_specialization(
6766                            node,
6767                            child,
6768                            self.source,
6769                            ancestry,
6770                        ) {
6771                            let code_unit = self.visit_named_class_like_shape(
6772                                recovered.declaration_node,
6773                                recovered.name,
6774                                None,
6775                                true,
6776                                Some(recovered.range),
6777                                None,
6778                                None,
6779                                &template_scope,
6780                                stack,
6781                                ancestry,
6782                            );
6783                            self.parsed.record_materialization(
6784                                MaterializationRecord::RecoveredDeclaration {
6785                                    recovery: recovered.range,
6786                                    unit: code_unit.clone(),
6787                                },
6788                            );
6789                            let mut member_scope = template_scope.clone();
6790                            member_scope.class_unit = Some(code_unit);
6791                            member_scope.declarations_are_fields = true;
6792                            member_scope.recovered_specialization_member_scope = true;
6793                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6794                                stack.push(CppWork::Node(CppNodeWork {
6795                                    node: prefix_member,
6796                                    scope: member_scope.clone(),
6797                                }));
6798                            }
6799                            for sibling in recovered.member_siblings {
6800                                self.recovered_class_sibling_scopes
6801                                    .insert(sibling.id(), member_scope.clone());
6802                            }
6803                            for following in recovered.following_declarations.into_iter().rev() {
6804                                stack.push(CppWork::Node(CppNodeWork {
6805                                    node: following,
6806                                    scope: scope.clone(),
6807                                }));
6808                            }
6809                            return;
6810                        }
6811                        stack.push(CppWork::Node(CppNodeWork {
6812                            node: child,
6813                            scope: template_scope,
6814                        }));
6815                    }
6816                }
6817            }
6818            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6819            "linkage_specification" => {
6820                if let Some(body) = cpp_body_node(node) {
6821                    // A linkage specification either wraps a direct declaration or a
6822                    // braced declaration_list. Visit the direct item itself so
6823                    // declaration recoveries still run on its original syntax node.
6824                    let work = if body.kind() == "declaration_list" {
6825                        CppWork::Container(CppContainer {
6826                            node: body,
6827                            scope: scope.clone(),
6828                        })
6829                    } else {
6830                        CppWork::Node(CppNodeWork {
6831                            node: body,
6832                            scope: scope.clone(),
6833                        })
6834                    };
6835                    stack.push(work);
6836                } else {
6837                    stack.push(CppWork::Container(CppContainer {
6838                        node,
6839                        scope: scope.clone(),
6840                    }));
6841                }
6842            }
6843            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6844                self.visit_class_like(node, scope, stack, ancestry)
6845            }
6846            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6847            // A bare namespace-begin sentinel can make tree-sitter promote the
6848            // wrapped declaration to an ERROR node instead of the usual bogus
6849            // function_definition envelope. Keep the recovery entry point on
6850            // the same structured path for both shapes; ordinary ERROR nodes
6851            // retain their declaration-preserving wrapper traversal when the
6852            // sentinel predicate does not match.
6853            "ERROR" => {
6854                // A standalone field-list invocation has no declaration wrapper.
6855                // Claim only an envelope consisting entirely of known invocations.
6856                if node.named_child_count() > 0
6857                    && node.children(&mut node.walk()).all(|child| {
6858                        child.kind() == "identifier"
6859                            && self
6860                                .object_macro_fields
6861                                .contains_key(node_text(child, self.source))
6862                    })
6863                    && self.visit_bare_object_macro_fields(node, scope)
6864                {
6865                    return;
6866                }
6867                self.visit_object_macro_error_classes(node, scope);
6868                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6869                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6870                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6871                        return;
6872                    }
6873                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6874                        return;
6875                    }
6876                    self.visit_macro_swallowed_function_declarations(node, scope);
6877                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6878                    self.visit_stranded_class_members(node, scope, ancestry);
6879                    stack.push(CppWork::Container(CppContainer {
6880                        node,
6881                        scope: scope.clone(),
6882                    }));
6883                }
6884            }
6885            "declaration" => {
6886                if node.has_error() {
6887                    // Object-like macro lines before a function-like
6888                    // export-macro class head keep the head in a `declaration`
6889                    // rather than in an `ERROR` (#2924). It is the same head
6890                    // and the same following-sibling body, so it takes the same
6891                    // recovery; without it the head's macro invocation indexes
6892                    // as a field named after the macro.
6893                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6894                        return;
6895                    }
6896                }
6897                // The macro-declaration grammar reads a K&R prototype macro
6898                // line as a clean declaration with the name and the macro
6899                // displaced into `macro_modifier` slots, so the prototype
6900                // candidate gate runs on clean declarations too; the candidates
6901                // themselves remain the admission proof.
6902                self.visit_prototype_macro_declarations(node, scope, ancestry);
6903                if self.node_is_inside_consumed_fragment(node) {
6904                    // The whole declaration was a recovered K&R
6905                    // prototype macro invocation (issue #2932); the
6906                    // reparse above already indexed its Function.
6907                    // Falling through to the ordinary declaration
6908                    // visitor would additionally mint the swapped-field
6909                    // wreckage this recovery exists to replace.
6910                    return;
6911                }
6912                if scope.class_unit.is_some()
6913                    && scope.declarations_are_fields
6914                    && scope.recovered_specialization_member_scope
6915                    && let Some(alias_name) =
6916                        recovered_using_declaration_alias_name(node, self.source)
6917                {
6918                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
6919                } else {
6920                    self.visit_declaration(
6921                        node,
6922                        scope,
6923                        scope.declarations_are_fields,
6924                        stack,
6925                        ancestry,
6926                    )
6927                }
6928            }
6929            // A K&R prototype macro invocation with a pointer return type and a
6930            // simple argument list parses with no ERROR node at all: tree-sitter
6931            // reads `T *name _(( args ));` as a multiplication of a type by a
6932            // call (`T * name_(...)`), wrapped in an `expression_statement`
6933            // that `has_error()` only because of a `MISSING "::"` inside it
6934            // (issue #2932). Nothing else mints a declaration from an
6935            // `expression_statement` at declaration scope today, so there is
6936            // no existing behavior to preserve here if this node is not the
6937            // K&R shape; the admission gate inside
6938            // `visit_prototype_macro_declarations` (exactly one clean function
6939            // prototype spanning the recovered run) is what keeps this arm
6940            // safe on an ordinary errorful expression statement that happens
6941            // to contain nested parentheses.
6942            "expression_statement" => {
6943                if node.has_error() {
6944                    self.visit_prototype_macro_declarations(node, scope, ancestry);
6945                }
6946            }
6947            "field_declaration" => {
6948                // A class-body K&R prototype macro line is the same
6949                // `macro_modifier` shape as a file-scope one, so it takes the
6950                // same candidate gate and reparse (#6902). Only this node's own
6951                // recovery ends its visit: a recovery walk can reach members of
6952                // a region another recovery consumed, and those stay walked.
6953                if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
6954                    self.visit_declaration(node, scope, true, stack, ancestry)
6955                }
6956            }
6957            "preproc_call" => self.visit_preproc_call(node),
6958            "type_definition" | "alias_declaration" => {
6959                self.visit_type_declaration(node, scope, stack, ancestry)
6960            }
6961            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
6962            // `#include` is collected by the coordinated primary events before the
6963            // walk, so a directive the container walk never reaches -- inside
6964            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
6965            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
6966            // include claim.
6967            "preproc_include" => {}
6968            // The grammar represents a split extern-C condition as sibling
6969            // markers; declarations between them remain unconditional file
6970            // scope items and need no special declaration metadata.
6971            "preproc_linkage_open" | "preproc_linkage_close" => {}
6972            kind if preserves_declaration_scope_through_wrapper(
6973                kind,
6974                scope.class_unit.is_some(),
6975            ) =>
6976            {
6977                // A preprocessor conditional gates every declaration inside it
6978                // on a configuration this analyzer never evaluates; record the
6979                // interval so declaration state can say so (issue #1476). The
6980                // else/elif branches are children of the `preproc_if` node, so
6981                // recording the openers covers every branch.
6982                if kind == "labeled_statement" {
6983                    self.visit_access_label_constructor(node, scope, ancestry);
6984                }
6985                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
6986                    let mut range = cpp_declaration_range(node);
6987                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
6988                        range.end_byte = boundary.end_byte;
6989                        range.end_line = boundary.end_line;
6990                    }
6991                    self.parsed.record_materialization(
6992                        MaterializationRecord::ConfigurationConditional { range },
6993                    );
6994                    if node.has_error() {
6995                        // A malformed export-macro class can close the namespace
6996                        // node early while the enclosing include guard still owns
6997                        // the remaining class-head/body pairs. The ordinary walk
6998                        // cannot carry the lost namespace through those promoted
6999                        // siblings. Scan only structured ERROR nodes in this
7000                        // already-malformed conditional; the pair recovery's
7001                        // exact class/macro/body predicate remains the admission
7002                        // gate, and its namespace lifting restores the owner.
7003                        let mut candidates = vec![node];
7004                        while let Some(candidate) = candidates.pop() {
7005                            // An `ERROR` still inside a namespace body is one
7006                            // the ordinary walk reaches with that namespace in
7007                            // scope. Claiming it here registers the recovered
7008                            // class at file scope and the consumed region then
7009                            // suppresses the walk that would have named it
7010                            // correctly -- which is why Botan's `DL_Group` was
7011                            // `DL_Group` and not `Botan.DL_Group` in the real
7012                            // header, where an include guard wraps the
7013                            // namespace, and was right in a fixture without one
7014                            // (#2552).
7015                            if candidate.kind() == "ERROR"
7016                                && !cpp_is_inside_namespace_body(candidate, ancestry)
7017                                && self.visit_function_like_export_class_pair(
7018                                    candidate, scope, stack, ancestry,
7019                                )
7020                            {
7021                                continue;
7022                            }
7023                            for index in (0..candidate.named_child_count()).rev() {
7024                                candidates.push(
7025                                    candidate
7026                                        .named_child(index)
7027                                        .expect("index below the node's own named child count"),
7028                                );
7029                            }
7030                        }
7031                    }
7032                }
7033                stack.push(CppWork::Container(CppContainer {
7034                    node,
7035                    scope: scope.clone(),
7036                }))
7037            }
7038            // A macro invocation the parser could not group leaves its head in
7039            // whatever slot was free. The `function_definition` and `ERROR`
7040            // arms above try the same recovery on the envelopes they get; a
7041            // flattened run leaves a bare `identifier`/`type_identifier`, and a
7042            // swallowed tail can land in a `parameter_declaration` (#3094).
7043            _ => {
7044                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7045            }
7046        }
7047    }
7048
7049    fn visit_macro_swallowed_function_declarations<'tree>(
7050        &mut self,
7051        envelope: Node<'tree>,
7052        scope: &ScopeInfo,
7053    ) {
7054        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7055            || envelope.kind() == "ERROR"
7056                && envelope
7057                    .parent()
7058                    .is_some_and(|parent| parent.kind() == "ERROR")
7059        {
7060            return;
7061        }
7062        let mut stack = (0..envelope.named_child_count())
7063            .filter_map(|index| envelope.named_child(index))
7064            .collect::<Vec<_>>();
7065        while let Some(node) = stack.pop() {
7066            if node.kind() == "function_declarator" {
7067                self.visit_error_swallowed_function_declaration(node, scope);
7068            }
7069            for child in named_children_iter(node) {
7070                stack.push(child);
7071            }
7072        }
7073    }
7074
7075    /// Index the declarations an attribute-like macro invocation swallowed into
7076    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
7077    /// the shape and why the parser produces it.
7078    fn visit_macro_wrapped_declarations<'tree>(
7079        &mut self,
7080        envelope: Node<'tree>,
7081        scope: &ScopeInfo,
7082        ancestry: &ParentIndex<'tree>,
7083    ) {
7084        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7085        if recovered.is_empty() {
7086            return;
7087        }
7088        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7089        self.record_recovered_declarations(recovery, envelope, |visitor| {
7090            for declaration in recovered {
7091                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7092            }
7093        });
7094    }
7095
7096    /// Index the declarations a macro invocation collapsed. See
7097    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7098    /// produces it. Returns whether it claimed the region beginning at
7099    /// `envelope`.
7100    ///
7101    /// The parser's own nodes cannot be read the way the swallowed tail of the
7102    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7103    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7104    /// with no declarator left at all, and the declarators that do survive on
7105    /// the envelope's declarator spine pair one declaration's name with the
7106    /// *next* declaration's parameter list. Reading those would be a guess.
7107    ///
7108    /// The bytes are still ordinary declarations, though, and the collapse is
7109    /// the parser carrying the failure forward from one macro invocation. So
7110    /// reparse the region, walk the items the parser makes of it, and when one
7111    /// item is itself a collapsed run, recover that invocation from its own
7112    /// bytes and resume the scan just past it. Each pass starts later than the
7113    /// last, so the scan is a loop over the invocations that collapse, not over
7114    /// the declarations: `llama.h` needs three passes for 214 declarations.
7115    ///
7116    /// The region can reach past `envelope` into the siblings the parser handed
7117    /// the rest of one invocation, so it is recorded as consumed: those
7118    /// siblings are this recovery's, and the ordinary walk must not read them
7119    /// again. Recording it after the scan, not before, keeps the scan's own
7120    /// reparsed nodes -- which carry their original byte offsets -- visible to
7121    /// the walk it drives.
7122    fn visit_collapsed_macro_declaration_run<'tree>(
7123        &mut self,
7124        envelope: Node<'tree>,
7125        scope: &ScopeInfo,
7126        ancestry: &ParentIndex<'tree>,
7127    ) -> bool {
7128        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7129            return false;
7130        };
7131        let start = envelope.start_byte();
7132        let end = run.region_end;
7133        let recovery = cpp_recovery_window(self.source, start, end);
7134        self.record_recovered_declarations(recovery, envelope, |visitor| {
7135            let mut position = start;
7136            while position < end {
7137                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7138                    return;
7139                };
7140                let root = tree.root_node();
7141                // A region reparse is its own tree and needs its own parent
7142                // index and dependency arena; the caller's answers nothing
7143                // about these nodes.
7144                let ancestry = visitor.recovery_ancestry(root);
7145                let mut cursor = root.walk();
7146                let collapsed =
7147                    root.named_children(&mut cursor)
7148                        .enumerate()
7149                        .find_map(|(index, item)| {
7150                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7151                                .map(|run| (index, item, run))
7152                        });
7153                // Walk only the items before the collapsed one. It swallowed
7154                // the rest of the region, so handing it to the ordinary walk
7155                // would re-enter this recovery on a region that starts where
7156                // this one did.
7157                let mut stack = Vec::new();
7158                push_cpp_sibling_range(
7159                    root,
7160                    0,
7161                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7162                    scope.clone(),
7163                    &mut stack,
7164                );
7165                visitor.drain_cpp_work(stack, &ancestry);
7166                let Some((_, item, run)) = collapsed else {
7167                    return;
7168                };
7169                // On its own bytes the invocation is the declaration-scope
7170                // shape `macro_wrapped_declarations` reads, so its wrapped
7171                // declaration needs no reader of its own here.
7172                if let Some(head) =
7173                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7174                {
7175                    let head_root = head.root_node();
7176                    let head_ancestry = visitor.recovery_ancestry(head_root);
7177                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7178                }
7179                assert!(
7180                    run.invocation_end > position,
7181                    "a collapsed run at {position} must end after the byte the scan resumed \
7182                     from, but ended at {}",
7183                    run.invocation_end
7184                );
7185                position = run.invocation_end;
7186            }
7187        });
7188        self.consumed_fragment_regions.push((start, end));
7189        true
7190    }
7191
7192    /// Index the members written under an access label that a class-scope
7193    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7194    /// for the shape.
7195    ///
7196    /// The members are whole in the source but not in this tree, and at file
7197    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7198    /// declaration, so the reparse keeps the class around them: the class head
7199    /// through its `{`, the label and its members, and the class's `}`, as one
7200    /// included-range parse at the original offsets. That is the parse the
7201    /// class gets with the invocation's bytes left out, which is what the
7202    /// replacement's own declarations would have to be read without anyway.
7203    /// The reparsed label is a real `access_specifier`, so the members carry the
7204    /// access they are written under. Only the members the label swallowed are
7205    /// walked; the class's other members are the ordinary walk's. When the
7206    /// reparse does not read the label back, nothing is published: the
7207    /// field this node would otherwise mint is named after the access keyword.
7208    fn visit_macro_swallowed_access_label_members<'tree>(
7209        &mut self,
7210        node: Node<'tree>,
7211        label: &CppSwallowedAccessLabel<'tree>,
7212        scope: &ScopeInfo,
7213        ancestry: &ParentIndex<'tree>,
7214    ) {
7215        let Some(body) = ancestry.parent(node) else {
7216            return;
7217        };
7218        let Some(class_like) = ancestry.parent(body) else {
7219            return;
7220        };
7221        // The reparse holds the class itself, so the recovery's source context
7222        // is the class's enclosing one; the class's own scope would name it
7223        // twice in the recovered members' lexical paths.
7224        let Some(class_parent) = ancestry.parent(class_like) else {
7225            return;
7226        };
7227        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7228            return;
7229        };
7230        if body.kind() != "field_declaration_list"
7231            || open.kind() != "{"
7232            || close.kind() != "}"
7233            || close.is_missing()
7234        {
7235            return;
7236        }
7237        let start = label.keyword.start_byte();
7238        let end = node.end_byte();
7239        let Some(tree) = parse_source_ranges_with_cancellation(
7240            &tree_sitter_cpp::LANGUAGE.into(),
7241            self.source,
7242            &[
7243                (class_like.start_byte(), open.end_byte()),
7244                (start, end),
7245                (close.start_byte(), close.end_byte()),
7246            ],
7247            None,
7248        ) else {
7249            return;
7250        };
7251        let root = tree.root_node();
7252        let Some(reparsed_body) = root
7253            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7254            .and_then(|brace| brace.parent())
7255            .filter(|parent| parent.kind() == "field_declaration_list")
7256        else {
7257            return;
7258        };
7259        let mut cursor = reparsed_body.walk();
7260        let members = reparsed_body
7261            .named_children(&mut cursor)
7262            .enumerate()
7263            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7264            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7265            .collect::<Vec<_>>();
7266        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7267            return;
7268        };
7269        if label_start != start {
7270            return;
7271        }
7272        let last = members.last().expect("the label is a member").0;
7273        let reparsed_ancestry = self.recovery_ancestry(root);
7274        let recovery = cpp_recovery_window(self.source, start, end);
7275        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7276            let mut stack = Vec::new();
7277            push_cpp_sibling_range(
7278                reparsed_body,
7279                first + 1,
7280                last + 1,
7281                scope.clone(),
7282                &mut stack,
7283            );
7284            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7285        });
7286    }
7287
7288    /// Index the members a string-argument attribute macro stranded in an
7289    /// `ERROR` inside a class body.
7290    ///
7291    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7292    /// grouping of the attributed member and of every member written after it
7293    /// until it recovers. The members are still whole `function_declarator`
7294    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7295    /// export-macro class such as Botan's `DL_Group` was indexed with no
7296    /// members at all (#2552).
7297    fn visit_stranded_class_members<'tree>(
7298        &mut self,
7299        node: Node<'tree>,
7300        scope: &ScopeInfo,
7301        ancestry: &ParentIndex<'tree>,
7302    ) {
7303        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7304            return;
7305        }
7306        for member in stranded_declaration_run(node, self.source).declarations {
7307            self.add_macro_wrapped_declaration(member, scope, ancestry);
7308        }
7309    }
7310
7311    /// Index the constructor an access label swallowed in a reparsed class
7312    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7313    ///
7314    /// The declarator is recovered by reparsing from that call to the end of
7315    /// the label's own statement, which is the same offset-preserving region
7316    /// reparse every other recovery here uses, so the recovered nodes carry
7317    /// their true source positions.
7318    fn visit_access_label_constructor<'tree>(
7319        &mut self,
7320        node: Node<'tree>,
7321        scope: &ScopeInfo,
7322        ancestry: &ParentIndex<'tree>,
7323    ) {
7324        let Some(class_unit) = scope.class_unit.clone() else {
7325            return;
7326        };
7327        if !scope.declarations_are_fields {
7328            return;
7329        }
7330        let class_name = class_unit.identifier().to_string();
7331        // Statement context reads pointer parameters as multiplication. Use the
7332        // same class-context validation as an unlabelled constructor statement.
7333        let statement = node.named_children(&mut node.walk()).find(|child| {
7334            cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7335        });
7336        if let Some(statement) = statement {
7337            self.visit_bare_constructor_expression(statement, scope, ancestry);
7338            return;
7339        }
7340        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7341        else {
7342            return;
7343        };
7344        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7345            return;
7346        };
7347        let root = tree.root_node();
7348        let Some(declarator) =
7349            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7350        else {
7351            return;
7352        };
7353        let reparsed_ancestry = self.recovery_ancestry(root);
7354        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7355        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7356        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7357        self.record_recovered_declarations(recovery, node, |visitor| {
7358            visitor.add_macro_wrapped_declaration(
7359                MacroWrappedDeclaration {
7360                    declarator,
7361                    range,
7362                    is_static: false,
7363                },
7364                scope,
7365                &reparsed_ancestry,
7366            );
7367        });
7368        // This constructor has been accounted for. Its statement-context
7369        // initializer declarators do not declare additional members.
7370        self.consumed_fragment_regions.push((start, range.end_byte));
7371    }
7372
7373    /// Index the members a string-argument attribute macro stranded inside the
7374    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7375    /// shape 3 under the new grammar).
7376    ///
7377    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7378    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7379    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7380    /// the member after it becomes the specifier's own `type` field. Neither
7381    /// member has a real declarator node -- an abstract function declarator in a
7382    /// `type_descriptor` carries only parameters -- but each member's source range
7383    /// is whole, so reparse it and read the declarator from the reparse, which is
7384    /// the same offset-preserving region reparse every other recovery here uses.
7385    /// The admission gate is the same one every reparse shares: exactly one clean
7386    /// callable declaration spanning the member's range.
7387    fn visit_string_attribute_macro_type_members<'tree>(
7388        &mut self,
7389        node: Node<'tree>,
7390        scope: &ScopeInfo,
7391    ) -> bool {
7392        if node.kind() != "field_declaration" {
7393            return false;
7394        }
7395        let Some(specifier) = node
7396            .child_by_field_name("type")
7397            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7398        else {
7399            return false;
7400        };
7401        if specifier
7402            .child_by_field_name("name")
7403            .is_none_or(|name| name.kind() != "identifier")
7404        {
7405            return false;
7406        }
7407        // The attributed member sits inside the argument `ERROR`: the string's
7408        // quote is the first child, the member's `;` the last, and its
7409        // type-descriptor is in between.
7410        let mut cursor = specifier.walk();
7411        let Some(argument_error) = specifier
7412            .named_children(&mut cursor)
7413            .find(|child| child.kind() == "ERROR")
7414        else {
7415            return false;
7416        };
7417        if argument_error
7418            .child(0)
7419            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7420            || argument_error
7421                .child(argument_error.child_count().saturating_sub(1))
7422                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7423        {
7424            return false;
7425        }
7426        let mut members: Vec<((usize, usize), String)> = Vec::new();
7427        for index in 0..argument_error.child_count() {
7428            let Some(child) = argument_error.child(index) else {
7429                continue;
7430            };
7431            if child.kind() == "type_descriptor" {
7432                let start = (0..index)
7433                    .rev()
7434                    .find_map(|previous| {
7435                        argument_error
7436                            .child(previous)
7437                            .filter(|sibling| sibling.kind() == "explicit")
7438                    })
7439                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7440                let Some(name) = child
7441                    .child_by_field_name("type")
7442                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7443                else {
7444                    return false;
7445                };
7446                members.push(((start, argument_error.end_byte()), name));
7447            }
7448        }
7449        let [(first_range, first_name)] = members.as_slice() else {
7450            return false;
7451        };
7452        let Some(second) = specifier
7453            .child_by_field_name("type")
7454            .filter(|type_node| type_node.kind() == "type_descriptor")
7455        else {
7456            return false;
7457        };
7458        let Some(second_name) = second
7459            .child_by_field_name("type")
7460            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7461        else {
7462            return false;
7463        };
7464        let members = [
7465            (*first_range, first_name.as_str()),
7466            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7467        ];
7468        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7469        self.record_recovered_declarations(recovery, node, |visitor| {
7470            for ((start, end), member_name) in members {
7471                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7472                    continue;
7473                };
7474                let root = tree.root_node();
7475                // The file-scope reparse of a member-shaped head is errorful
7476                // (a member qualifier is not valid there), so the admission is
7477                // the declarator itself: exactly one function declarator
7478                // naming the member inside its own range.
7479                let mut candidate = None;
7480                let mut stack = vec![root];
7481                while let Some(current) = stack.pop() {
7482                    if current.kind() == "function_declarator"
7483                        && current.start_byte() >= start
7484                        && current.end_byte() <= end
7485                        && cpp_function_declarator_name_node(current)
7486                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7487                    {
7488                        if candidate.is_some() {
7489                            candidate = None;
7490                            break;
7491                        }
7492                        candidate = Some(current);
7493                        continue;
7494                    }
7495                    let mut cursor = current.walk();
7496                    stack.extend(current.named_children(&mut cursor));
7497                }
7498                let Some(declarator) = candidate else {
7499                    continue;
7500                };
7501                let reparsed_ancestry = visitor.recovery_ancestry(root);
7502                visitor.add_macro_wrapped_declaration(
7503                    MacroWrappedDeclaration {
7504                        declarator,
7505                        range: cpp_recovery_window(visitor.source, start, end),
7506                        is_static: false,
7507                    },
7508                    scope,
7509                    &reparsed_ancestry,
7510                );
7511            }
7512        });
7513        true
7514    }
7515
7516    fn add_macro_wrapped_declaration<'tree>(
7517        &mut self,
7518        declaration: MacroWrappedDeclaration<'tree>,
7519        scope: &ScopeInfo,
7520        ancestry: &ParentIndex<'tree>,
7521    ) {
7522        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7523        else {
7524            return;
7525        };
7526        let code_unit =
7527            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7528        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7529        let is_constructor = scope
7530            .class_unit
7531            .as_ref()
7532            .is_some_and(|class| function.name == class.identifier())
7533            || cpp_callable_is_structural_constructor(
7534                declaration.declarator,
7535                self.source,
7536                ancestry,
7537            );
7538        let already_declared = self.parsed.contains_declaration(&code_unit);
7539        if already_declared {
7540            self.parsed
7541                .record_navigation_range(code_unit.clone(), declaration.range);
7542            if definition.is_none() {
7543                return;
7544            }
7545            // Preserve the earlier declaration-only occurrence while adding
7546            // the recovered body as a distinct definition occurrence.
7547        }
7548        self.add_declaration_with_range(
7549            ancestry,
7550            code_unit.clone(),
7551            declaration.range,
7552            Some(declaration.declarator),
7553            None,
7554            None,
7555        );
7556        if definition.is_some() {
7557            let recovered_declaration = *self
7558                .source_links
7559                .get(&code_unit)
7560                .and_then(|links| links.last())
7561                .expect("recovered definition source");
7562            let mut properties = self.source_properties.borrow_mut();
7563            let fact = properties
7564                .get_mut(&recovered_declaration)
7565                .expect("recovered definition source fact");
7566            fact.occurrence_role =
7567                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7568            fact.callable_is_constructor |= is_constructor;
7569        }
7570        let signature = normalize_cpp_whitespace(
7571            self.source
7572                .get(declaration.range.start_byte..declaration.range.end_byte)
7573                .expect("a recovered declaration range covers one source range"),
7574        );
7575        let linkage = if declaration.is_static {
7576            CallableLinkage::Internal
7577        } else {
7578            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7579        };
7580        // Whether this is a definition is a property of the recovered node, not
7581        // of the caller: a declarator that a `function_definition` gives a body
7582        // is a definition wherever the recovery found it.
7583        let declaration_only = definition.is_none();
7584        self.add_signature_with_metadata(
7585            code_unit.clone(),
7586            cpp_signature_metadata(
7587                signature,
7588                declaration.declarator,
7589                linkage,
7590                self.source,
7591                ancestry,
7592            )
7593            .with_declaration_only(declaration_only),
7594        );
7595        if already_declared {
7596            return;
7597        }
7598        if let Some(parent) = &scope.class_unit {
7599            self.parsed.add_child(parent.clone(), code_unit);
7600        } else if let Some(module) = &scope.module {
7601            self.parsed.add_child(module.clone(), code_unit);
7602        }
7603    }
7604
7605    /// Index the local types of an ordinary function body. Function bodies are
7606    /// otherwise outside the declaration walk, but their anonymous aggregate
7607    /// owners must exist so structured local binding inference can name their
7608    /// fields (#2994), and a named C++ local class and its members are query
7609    /// targets of their own (#6902).
7610    fn visit_local_types_in_function<'tree>(
7611        &mut self,
7612        function: Node<'tree>,
7613        callable: &CodeUnit,
7614        scope: &ScopeInfo,
7615        stack: &mut Vec<CppWork<'tree>>,
7616        ancestry: &ParentIndex<'tree>,
7617    ) {
7618        let Some(body) = cpp_body_node(function) else {
7619            return;
7620        };
7621        // A body opens a block scope, so nothing declared in it is a member of
7622        // the class the definition may be written inside. Dropping the
7623        // aggregate carrier gives an inline member function's body the same
7624        // block scope an out-of-line `void C::f()` body already has; without
7625        // it a C++ header, where member bodies are usually inline, would keep
7626        // none of these owners (#3301).
7627        let block_scope;
7628        let scope = if scope.class_unit.is_some() {
7629            block_scope = ScopeInfo {
7630                class_unit: None,
7631                declarations_are_fields: false,
7632                ..scope.clone()
7633            };
7634            &block_scope
7635        } else {
7636            scope
7637        };
7638        let mut pending = vec![body];
7639        while let Some(node) = pending.pop() {
7640            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7641                continue;
7642            }
7643            if node.kind() == "declaration"
7644                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7645            {
7646                continue;
7647            }
7648            if matches!(
7649                node.kind(),
7650                "class_specifier" | "struct_specifier" | "union_specifier"
7651            ) {
7652                self.visit_function_local_class(node, callable, scope, stack, ancestry);
7653                continue;
7654            }
7655            let mut cursor = node.walk();
7656            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7657            children.reverse();
7658            pending.extend(children);
7659        }
7660    }
7661
7662    /// Mint a named C++ class defined in a function body under the identity
7663    /// of the block it is written in (see [`CppLocalBlock`]). Its members are
7664    /// owned by the class through the ordinary class-body walk.
7665    ///
7666    /// C block-scope tags are a separate capability and keep their current
7667    /// absence. A header is read both ways, so a minted local class is a
7668    /// declaration whose existence differs between the two readings, which is
7669    /// what the C-reading witness records.
7670    fn visit_function_local_class<'tree>(
7671        &mut self,
7672        node: Node<'tree>,
7673        callable: &CodeUnit,
7674        scope: &ScopeInfo,
7675        stack: &mut Vec<CppWork<'tree>>,
7676        ancestry: &ParentIndex<'tree>,
7677    ) {
7678        if self.c_tag_semantics || cpp_body_node(node).is_none() {
7679            return;
7680        }
7681        let Some(name) = class_like_name(node, self.source, ancestry) else {
7682            return;
7683        };
7684        let block =
7685            std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7686                .find(|ancestor| ancestor.kind() == "compound_statement")
7687                .expect("a function-local class is written inside the function body");
7688        self.c_tag_scope_witness = true;
7689        let local_scope = ScopeInfo {
7690            package_name: callable.package_name().to_string(),
7691            class_unit: None,
7692            template_signature: None,
7693            template_metadata: None,
7694            declarations_are_fields: false,
7695            recovered_specialization_member_scope: false,
7696            local_block: Some(CppLocalBlock::new(callable, block)),
7697            ..scope.clone()
7698        };
7699        self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7700    }
7701
7702    fn visit_error_swallowed_function_declaration<'tree>(
7703        &mut self,
7704        node: Node<'tree>,
7705        scope: &ScopeInfo,
7706    ) {
7707        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7708            return;
7709        };
7710        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7711            return;
7712        };
7713        let root = tree.root_node();
7714        let mut cursor = root.walk();
7715        let declarations = root
7716            .named_children(&mut cursor)
7717            .filter(|child| child.kind() != "comment")
7718            .collect::<Vec<_>>();
7719        let [declaration] = declarations.as_slice() else {
7720            return;
7721        };
7722        if declaration.kind() != "declaration"
7723            || declaration.has_error()
7724            || declaration.start_byte() != start
7725            || declaration.end_byte() != end
7726        {
7727            return;
7728        }
7729        let recovery = cpp_recovery_window(self.source, start, end);
7730        // The reparsed region is its own tree, so this walk indexes it itself.
7731        let reparsed_ancestry = self.recovery_ancestry(root);
7732        self.record_recovered_declarations(recovery, node, |visitor| {
7733            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7734        });
7735    }
7736
7737    /// Recover a K&R-compatibility prototype macro invocation such as
7738    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7739    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7740    /// defined only in headers outside this workspace (ruby's own
7741    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7742    /// `name _((args))` and turns each such line into a malformed
7743    /// `declaration` or pointer-expression statement. Before this
7744    /// recovery, the ordinary declaration visitor read that wreckage as a
7745    /// field with the type and name swapped (e.g. a field literally named
7746    /// `VALUE`) and indexed no prototype for the real name at all.
7747    ///
7748    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7749    /// preserve the declared name, a known one-argument compatibility macro,
7750    /// the nested argument list, and a live terminator. It rejects arbitrary
7751    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7752    /// guessing there previously turned malformed non-macro declarations into
7753    /// invented functions. For each proven candidate this reparses the range
7754    /// before the macro token, the inner parenthesized argument node, and the
7755    /// terminating `;` as one included-range parse. That is the parser-native
7756    /// equivalent of expanding `#define _(args) args` while retaining original
7757    /// byte offsets. The result is admitted only when it is exactly one clean
7758    /// function prototype spanning the recovered run.
7759    ///
7760    /// A class-body line is a `field_declaration`, and a bodiless member read
7761    /// at file scope loses the class-body position its `static` and access
7762    /// facts depend on. That reparse therefore keeps the class around the
7763    /// prototype, as [`PrototypeMacroClassEnvelope`] describes, and admits
7764    /// exactly one clean member prototype in the reparsed body instead.
7765    ///
7766    /// Returns whether a candidate was recovered.
7767    fn visit_prototype_macro_declarations<'tree>(
7768        &mut self,
7769        node: Node<'tree>,
7770        scope: &ScopeInfo,
7771        ancestry: &ParentIndex<'tree>,
7772    ) -> bool {
7773        let candidates = cpp_prototype_macro_candidates(node, self.source);
7774        if candidates.is_empty() {
7775            return false;
7776        }
7777        let envelope = if node.kind() == "field_declaration" {
7778            let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7779            else {
7780                return false;
7781            };
7782            Some(envelope)
7783        } else {
7784            None
7785        };
7786        let mut recovered = false;
7787        for candidate in candidates {
7788            let start = candidate.run_start;
7789            let end = candidate.semicolon_end;
7790            if self.byte_range_is_inside_consumed_fragment(start, end) {
7791                continue;
7792            }
7793            let mut ranges = Vec::with_capacity(6);
7794            if let Some(envelope) = &envelope {
7795                ranges.push(envelope.head);
7796                ranges.extend(envelope.label);
7797            }
7798            ranges.extend(candidate.ranges());
7799            if let Some(envelope) = &envelope {
7800                ranges.push(envelope.close);
7801            }
7802            let Some(tree) = parse_source_ranges_with_cancellation(
7803                &tree_sitter_cpp::LANGUAGE.into(),
7804                self.source,
7805                &ranges,
7806                None,
7807            ) else {
7808                continue;
7809            };
7810            let root = tree.root_node();
7811            let (container, expected_kind) = match &envelope {
7812                None => (root, "declaration"),
7813                Some(envelope) => {
7814                    let Some(body) = root
7815                        .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7816                        .and_then(|brace| brace.parent())
7817                        .filter(|parent| parent.kind() == "field_declaration_list")
7818                    else {
7819                        continue;
7820                    };
7821                    (body, "field_declaration")
7822                }
7823            };
7824            // Everything the envelope contributes ends before the prototype.
7825            let mut cursor = container.walk();
7826            let declarations = container
7827                .named_children(&mut cursor)
7828                .enumerate()
7829                .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7830                .collect::<Vec<_>>();
7831            let [(index, declaration)] = declarations.as_slice() else {
7832                continue;
7833            };
7834            if declaration.kind() != expected_kind
7835                || declaration.has_error()
7836                || declaration.start_byte() != start
7837                || declaration.end_byte() != end
7838                || declaration
7839                    .child_by_field_name("declarator")
7840                    .and_then(extract_function_declarator)
7841                    .is_none()
7842            {
7843                continue;
7844            }
7845            let index = *index;
7846            let recovery = cpp_recovery_window(self.source, start, end);
7847            // The reparsed region is its own tree, so this walk indexes it
7848            // itself. A class envelope holds the class, so the recovery's
7849            // source context is the class's enclosing one; the class's own
7850            // would name it twice in the member's lexical path.
7851            let reparsed_ancestry = self.recovery_ancestry(root);
7852            let origin = envelope
7853                .as_ref()
7854                .map_or(node, |envelope| envelope.class_parent);
7855            self.record_recovered_declarations(recovery, origin, |visitor| {
7856                let mut stack = Vec::new();
7857                push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7858                visitor.drain_cpp_work(stack, &reparsed_ancestry);
7859            });
7860            self.consumed_fragment_regions.push((start, end));
7861            recovered = true;
7862        }
7863        recovered
7864    }
7865
7866    /// Declare one Module per namespace level of `levels` under `package_name`,
7867    /// each recording the range of the construct that writes it, and return the
7868    /// innermost level's package name and Module. A level an earlier definition
7869    /// already declared is reused, which is also how a reopened namespace keeps
7870    /// the range of the definition that first declared it.
7871    fn declare_namespace_levels(
7872        &mut self,
7873        mut package_name: String,
7874        levels: Vec<(String, Range, Option<Range>)>,
7875        node: Node<'_>,
7876        ancestry: &ParentIndex<'_>,
7877    ) -> (String, Option<CodeUnit>) {
7878        let mut module = None;
7879        for (component, range, name_range) in levels {
7880            let full_name = if package_name.is_empty() {
7881                component
7882            } else {
7883                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7884            };
7885            let level = CodeUnit::new_fq(
7886                self.file.clone(),
7887                CodeUnitType::Module,
7888                "",
7889                full_name.clone(),
7890                cpp_namespace_fq(&full_name),
7891            );
7892            if !self.parsed.contains_declaration(&level) {
7893                if let Some(name_range) = name_range {
7894                    self.add_declaration_with_name_range(
7895                        level.clone(),
7896                        range,
7897                        name_range,
7898                        Some((ancestry, node)),
7899                        None,
7900                        None,
7901                    );
7902                } else {
7903                    self.add_declaration_with_range(
7904                        ancestry,
7905                        level.clone(),
7906                        range,
7907                        Some(node),
7908                        None,
7909                        None,
7910                    );
7911                }
7912            }
7913            package_name = full_name;
7914            module = Some(level);
7915        }
7916        (package_name, module)
7917    }
7918
7919    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
7920    /// index restores. Each level carries the range of its own name token,
7921    /// which the walk's current node cannot supply: that node is whichever
7922    /// declaration the walk reached first inside the region, and deriving a
7923    /// level's name from it paired the level's range with an unrelated
7924    /// construct's name token (#4292).
7925    fn declare_recovered_namespace_levels(
7926        &mut self,
7927        mut package_name: String,
7928        levels: Vec<(String, Range, Range)>,
7929    ) -> (String, Option<CodeUnit>) {
7930        let mut module = None;
7931        for (component, range, name_range) in levels {
7932            let full_name = if package_name.is_empty() {
7933                component
7934            } else {
7935                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7936            };
7937            let level = CodeUnit::new_fq(
7938                self.file.clone(),
7939                CodeUnitType::Module,
7940                "",
7941                full_name.clone(),
7942                cpp_namespace_fq(&full_name),
7943            );
7944            if !self.parsed.contains_declaration(&level) {
7945                self.add_declaration_with_name_range(
7946                    level.clone(),
7947                    range,
7948                    name_range,
7949                    None,
7950                    None,
7951                    None,
7952                );
7953            }
7954            package_name = full_name;
7955            module = Some(level);
7956        }
7957        (package_name, module)
7958    }
7959
7960    /// Restore the complete lexical namespace path when recovery dropped an
7961    /// ancestor or retained it past its real close. Parsed namespaces beginning
7962    /// inside the recovered region still contribute their normal nesting.
7963    /// Crossing that boundary also clears any malformed class owner.
7964    fn recovered_namespace_scope(
7965        &mut self,
7966        node: Node<'_>,
7967        scope: &ScopeInfo,
7968    ) -> Option<ScopeInfo> {
7969        self.orphaned_namespaces.region_at(node.start_byte())?;
7970        // Each level carries the `namespace Name { ... }` construct that writes
7971        // it along with the level's own name token. Declaring them at `node`
7972        // instead recorded the range of whichever member the walk happened to
7973        // reach first, so a namespace whose head collapsed into an ERROR
7974        // reported its first inner declaration's lines as its own (#3309), and
7975        // deriving the name from that node paired the level's range with the
7976        // member's name token (#4292).
7977        let levels = self
7978            .orphaned_namespaces
7979            .enclosing_namespace_levels(node, self.source);
7980        let package_name = levels
7981            .iter()
7982            .map(|(component, ..)| component.as_str())
7983            .collect::<Vec<_>>()
7984            .join(CPP_PACKAGE_SEPARATOR);
7985        if package_name == scope.package_name {
7986            return None;
7987        }
7988        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
7989        Some(ScopeInfo {
7990            package_name,
7991            module,
7992            // A recovered namespace body is no longer inside the class (or
7993            // function) that tree-sitter accidentally used as its wrapper.
7994            // Keep namespace/module context, but never carry that owner into
7995            // declarations published from the recovered region.
7996            class_unit: None,
7997            template_signature: None,
7998            template_metadata: None,
7999            declarations_are_fields: false,
8000            recovered_specialization_member_scope: false,
8001            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8002            local_block: None,
8003            visible_using_declarations: scope.visible_using_declarations.clone(),
8004        })
8005    }
8006
8007    fn visit_namespace<'tree>(
8008        &mut self,
8009        node: Node<'tree>,
8010        scope: &ScopeInfo,
8011        stack: &mut Vec<CppWork<'tree>>,
8012        ancestry: &ParentIndex<'tree>,
8013    ) {
8014        let name_node = node.child_by_field_name("name");
8015        let Some(name_node) = name_node else {
8016            if let Some(body) = cpp_body_node(node) {
8017                stack.push(CppWork::Container(CppContainer {
8018                    node: body,
8019                    scope: scope.clone(),
8020                }));
8021            }
8022            return;
8023        };
8024        // Diagnostic corpora contain deliberately ill-formed global namespace
8025        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
8026        // the leading global `::` as the first anonymous child. Honor that AST
8027        // boundary instead of appending the name to the lexical namespace;
8028        // appending produced legacy names such as `outer::::outer::inner`, which
8029        // could not round-trip through the structured FqName boundary.
8030        let explicitly_global = name_node
8031            .child(0)
8032            .is_some_and(|child| !child.is_named() && child.kind() == "::");
8033        let components = cpp_namespace_name_components(name_node, self.source);
8034        if components.is_empty() {
8035            return;
8036        }
8037        // One Module per namespace level. C++17's `namespace a::b { ... }` is
8038        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
8039        // shorthand must declare `a` as well as `a::b` -- extracting only the
8040        // innermost level left the enclosing namespace undeclared and made the
8041        // two spellings of one construct disagree (issue #1878).
8042        let package_name = if explicitly_global {
8043            String::new()
8044        } else {
8045            scope.package_name.clone()
8046        };
8047        let definition = node_range(node);
8048        // The `namespace a::b` shorthand names every level in one node: each
8049        // level's name occurrence is its own token, the same evidence the
8050        // recovered-namespace path carries per level (#4292).
8051        let mut component_ranges =
8052            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8053        let (package_name, module) = self.declare_namespace_levels(
8054            package_name,
8055            components
8056                .into_iter()
8057                .map(|component| {
8058                    let name_range = component_ranges
8059                        .next()
8060                        .filter(|(name, _)| *name == component)
8061                        .map(|(_, range)| range);
8062                    (component, definition, name_range)
8063                })
8064                .collect(),
8065            node,
8066            ancestry,
8067        );
8068
8069        let namespace_scope = ScopeInfo {
8070            package_name,
8071            module,
8072            // C++ never nests a namespace inside a class, so a surviving
8073            // class_unit here is always recovery bleed: a malformed-region
8074            // boundary upstream mis-scoped this namespace block. Keeping the
8075            // owner would mint the namespace's declarations as class members
8076            // under a re-appended package, desyncing the fq boundary assert
8077            // (#2306). Dropping it is identity-neutral for valid code, where
8078            // class_unit is always empty at a namespace definition.
8079            class_unit: None,
8080            template_signature: scope.template_signature.clone(),
8081            template_metadata: scope.template_metadata.clone(),
8082            declarations_are_fields: false,
8083            recovered_specialization_member_scope: false,
8084            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8085            local_block: None,
8086            visible_using_declarations: scope.visible_using_declarations.clone(),
8087        };
8088        let container = cpp_body_node(node).unwrap_or(node);
8089        // A malformed export-macro class body may turn the following class
8090        // into a descendant of a bogus function/labeled/error envelope. Those
8091        // descendants are not declaration containers and the ordinary walk
8092        // intentionally does not descend into them. Scan the namespace tree
8093        // once for the strict embedded class geometry before scheduling its
8094        // normal declarations. When one envelope matches, its helper recovers
8095        // every embedded class and the walk need not inspect its descendants.
8096        let mut candidates = vec![container];
8097        while let Some(candidate) = candidates.pop() {
8098            if matches!(
8099                candidate.kind(),
8100                "ERROR" | "function_definition" | "labeled_statement"
8101            ) && self.visit_embedded_function_like_export_classes(
8102                candidate,
8103                &namespace_scope,
8104                stack,
8105                ancestry,
8106            ) {
8107                continue;
8108            }
8109            for index in (0..candidate.named_child_count()).rev() {
8110                candidates.push(
8111                    candidate
8112                        .named_child(index)
8113                        .expect("index below the node's own named child count"),
8114                );
8115            }
8116        }
8117        stack.push(CppWork::Container(CppContainer {
8118            node: container,
8119            scope: namespace_scope,
8120        }));
8121    }
8122
8123    fn visit_class_like<'tree>(
8124        &mut self,
8125        node: Node<'tree>,
8126        scope: &ScopeInfo,
8127        stack: &mut Vec<CppWork<'tree>>,
8128        ancestry: &ParentIndex<'tree>,
8129    ) {
8130        let Some(name) = class_like_name(node, self.source, ancestry) else {
8131            return;
8132        };
8133        let name = qualified_class_name_chain(node, self.source, scope)
8134            .map(|chain| chain.join("$"))
8135            .unwrap_or(name);
8136        self.visit_named_class_like(node, name, scope, stack, ancestry);
8137    }
8138
8139    fn visit_named_class_like<'tree>(
8140        &mut self,
8141        node: Node<'tree>,
8142        name: String,
8143        scope: &ScopeInfo,
8144        stack: &mut Vec<CppWork<'tree>>,
8145        ancestry: &ParentIndex<'tree>,
8146    ) {
8147        let body = cpp_body_node(node);
8148        let definition_body_present = body.is_some();
8149        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8150            .then(|| extract_cpp_supertypes(node, self.source));
8151        self.visit_named_class_like_shape(
8152            node,
8153            name,
8154            body,
8155            definition_body_present,
8156            None,
8157            raw_supertypes,
8158            Some(extract_cpp_base_facts(node, self.source)),
8159            scope,
8160            stack,
8161            ancestry,
8162        );
8163    }
8164
8165    /// Whether this class-like declaration is a C tag that belongs to the
8166    /// enclosing non-aggregate scope rather than to the aggregate it is
8167    /// lexically written inside.
8168    ///
8169    /// `class_specifier` is deliberately excluded: `class` is not C, so text
8170    /// that spells one in a `.c` file is not C code and keeps the C++ reading
8171    /// rather than getting a half-C identity.
8172    fn mints_tag_at_enclosing_c_scope(
8173        &self,
8174        declaration_node: Node<'_>,
8175        scope: &ScopeInfo,
8176        ancestry: &ParentIndex<'_>,
8177    ) -> bool {
8178        if scope.class_unit.is_none()
8179            || !matches!(
8180                declaration_node.kind(),
8181                "struct_specifier" | "union_specifier" | "enum_specifier"
8182            )
8183            || class_like_name(declaration_node, self.source, ancestry).is_none()
8184        {
8185            return false;
8186        }
8187        // A class-specifier ancestor proves this is C++ syntax. A C reading of
8188        // the header must not apply C tag hoisting to structs nested in that
8189        // class, because that invents a file-scope identity for its members.
8190        let mut ancestor = ancestry.parent(declaration_node);
8191        while let Some(current) = ancestor {
8192            if current.kind() == "class_specifier" {
8193                return false;
8194            }
8195            ancestor = ancestry.parent(current);
8196        }
8197        true
8198    }
8199
8200    #[allow(clippy::too_many_arguments)]
8201    fn visit_named_class_like_shape<'tree>(
8202        &mut self,
8203        declaration_node: Node<'tree>,
8204        name: String,
8205        body: Option<Node<'tree>>,
8206        definition_body_present: bool,
8207        explicit_range: Option<Range>,
8208        raw_supertypes: Option<Vec<String>>,
8209        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8210        scope: &ScopeInfo,
8211        stack: &mut Vec<CppWork<'tree>>,
8212        ancestry: &ParentIndex<'tree>,
8213    ) -> CodeUnit {
8214        let displaced_macro_tail = if explicit_range.is_none() {
8215            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8216        } else {
8217            None
8218        };
8219        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8220        let recovered_scope = self.scope_for_recovered_exported_class(
8221            declaration_node,
8222            &name,
8223            definition_body_present,
8224            scope,
8225            ancestry,
8226        );
8227        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8228        // aggregate's member list is declared at the enclosing non-aggregate
8229        // scope, not nested inside the aggregate. `scope.class_unit` is the
8230        // only aggregate carrier in this walk, so dropping it puts the tag at
8231        // the nearest enclosing non-aggregate scope -- the module at file or
8232        // namespace scope, and the same block-scope representation a
8233        // function-local aggregate already gets. The tag's own body scope
8234        // below still owns its members, so fields and enumerators are
8235        // unaffected.
8236        let c_tag_scope;
8237        let c_tag_scope_applies =
8238            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8239        if !self.c_tag_semantics && c_tag_scope_applies {
8240            self.c_tag_scope_witness = true;
8241        }
8242        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8243            c_tag_scope = ScopeInfo {
8244                class_unit: None,
8245                ..recovered_scope.clone()
8246            };
8247            &c_tag_scope
8248        } else {
8249            &recovered_scope
8250        };
8251        let local_block = scope
8252            .local_block
8253            .as_ref()
8254            .filter(|_| scope.class_unit.is_none());
8255        let short_name = if let Some(parent) = &scope.class_unit {
8256            cpp_join_nested_short(parent.short_name(), &name)
8257        } else if let Some(block) = local_block {
8258            cpp_join_nested_short(&block.short_name, &name)
8259        } else {
8260            name.clone()
8261        };
8262        // A top-level out-of-line qualified class definition (`struct
8263        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8264        // nesting chain as the `$`-joined display name; push one Type/Nested
8265        // segment per class so segment-pop owner navigation keeps working.
8266        // Every other leaf name stays opaque so a literal `$` in a source
8267        // identifier never crosses the split/join boundary (#2140).
8268        let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8269            qualified_class_name_chain(declaration_node, self.source, scope)
8270                .filter(|chain| chain.join("$") == name)
8271        } else {
8272            None
8273        };
8274        let fq = if let Some(block) = local_block {
8275            block
8276                .fq
8277                .clone()
8278                .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8279        } else if let Some(chain) = qualified_chain {
8280            let mut fq = FqName::new();
8281            cpp_push_package(&mut fq, &scope.package_name);
8282            let mut first = true;
8283            for component in chain {
8284                let kind = if first {
8285                    SegmentKind::Type
8286                } else {
8287                    SegmentKind::Nested
8288                };
8289                fq.push(segment_interner().intern(&component, kind));
8290                first = false;
8291            }
8292            fq
8293        } else {
8294            cpp_leaf_fq(
8295                &scope.package_name,
8296                scope.class_unit.as_ref(),
8297                &name,
8298                SegmentKind::Nested,
8299                SegmentKind::Type,
8300            )
8301        };
8302        let code_unit = CodeUnit::with_signature_and_fq(
8303            self.file.clone(),
8304            CodeUnitType::Class,
8305            scope.package_name.clone(),
8306            short_name,
8307            scope.template_signature.clone(),
8308            false,
8309            fq,
8310        );
8311        let has_body = definition_body_present;
8312        if !has_body && self.parsed.contains_declaration(&code_unit) {
8313            if let Some(range) = explicit_range {
8314                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8315            } else {
8316                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8317            }
8318            self.parsed.record_navigation_range(
8319                code_unit.clone(),
8320                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8321            );
8322            return code_unit;
8323        }
8324        if has_body {
8325            // A forward declaration above this definition is one of the
8326            // class's declaration sites: C++ injects the class name at the
8327            // forward declaration, so a reference between the two spells this
8328            // class (#3297). Moving the unit to its body must not drop the
8329            // earlier site, exactly as a function prototype and its body stay
8330            // two ranges of one declaration (#1650).
8331            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8332            if let Some(range) = explicit_range {
8333                self.replace_declaration_with_range_deferred(
8334                    ancestry,
8335                    code_unit.clone(),
8336                    range,
8337                    Some(declaration_node),
8338                    None,
8339                    None,
8340                );
8341            } else {
8342                self.replace_declaration_deferred(
8343                    ancestry,
8344                    code_unit.clone(),
8345                    declaration_node,
8346                    None,
8347                    None,
8348                );
8349            }
8350            for range in earlier_ranges {
8351                self.parsed.add_declaration_range(&code_unit, range);
8352            }
8353        } else {
8354            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8355        }
8356        if let Some(declaration) = self
8357            .source_links
8358            .get(&code_unit)
8359            .and_then(|links| links.last())
8360            .copied()
8361        {
8362            let mut properties = self.source_properties.borrow_mut();
8363            let fact = properties
8364                .get_mut(&declaration)
8365                .expect("linked class has source properties");
8366            fact.occurrence_role = if has_body {
8367                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8368            } else {
8369                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8370            };
8371            fact.class_strength = if has_body {
8372                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8373            } else {
8374                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8375            };
8376            if let Some(base_facts) = base_facts.as_ref() {
8377                fact.bases = base_facts.clone();
8378            }
8379        }
8380        if let Some(raw_supertypes) = raw_supertypes {
8381            self.parsed
8382                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8383        }
8384        self.parsed.add_signature(
8385            code_unit.clone(),
8386            render_cpp_type_signature(
8387                declaration_node,
8388                self.source,
8389                scope.template_signature.as_deref(),
8390            ),
8391        );
8392        if let Some(metadata) = &scope.template_metadata {
8393            let primary_short_name = if let Some(parent) = &scope.class_unit {
8394                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8395            } else {
8396                metadata.primary_name.clone()
8397            };
8398            let primary_fq_name = CodeUnit::new(
8399                self.file.clone(),
8400                CodeUnitType::Class,
8401                scope.package_name.clone(),
8402                primary_short_name,
8403            )
8404            .fq_name();
8405            let mut metadata = metadata.clone();
8406            metadata.primary_fq_name = primary_fq_name;
8407            self.parsed
8408                .set_cpp_template_metadata(code_unit.clone(), metadata);
8409        }
8410        if let Some(parent) = &scope.class_unit {
8411            self.parsed.add_child(parent.clone(), code_unit.clone());
8412        } else if let Some(block) = local_block {
8413            self.parsed
8414                .add_child(block.callable.clone(), code_unit.clone());
8415        } else if let Some(module) = &scope.module {
8416            self.parsed.add_child(module.clone(), code_unit.clone());
8417        }
8418
8419        if let Some(body) = body {
8420            let mut nested_scope = scope.clone();
8421            nested_scope.class_unit = Some(code_unit.clone());
8422            nested_scope.local_block = None;
8423            nested_scope.template_signature = scope.template_signature.clone();
8424            // Template metadata describes the class just created. It must not
8425            // leak into ordinary nested declarations in that class's body.
8426            // Recovered export-macro specializations carry a separate scope bit
8427            // for their declaration-shaped body members.
8428            nested_scope.template_metadata = None;
8429            // Export-macro class bodies recovered from a function_definition use
8430            // compound_statement children, whose direct fields are declarations.
8431            nested_scope.recovered_specialization_member_scope =
8432                scope.template_metadata.as_ref().is_some_and(|metadata| {
8433                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8434                });
8435            nested_scope.declarations_are_fields =
8436                is_recovered_exported_class_container(declaration_node, self.source)
8437                    || nested_scope.recovered_specialization_member_scope;
8438            if let Some(displaced) = displaced_macro_tail {
8439                // A macro-shaped field without a source semicolon can make
8440                // tree-sitter consume the real class terminator as an ERROR
8441                // inside that field, then retain following namespace items as
8442                // later field-list children. Drain the proven class prefix
8443                // first and re-own only the structured tail with the outer
8444                // scope. The tail is pushed first because the work stack is
8445                // LIFO.
8446                push_cpp_sibling_range(
8447                    body,
8448                    displaced.split_index,
8449                    usize::MAX,
8450                    scope.clone(),
8451                    stack,
8452                );
8453                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8454            } else {
8455                stack.push(CppWork::Container(CppContainer {
8456                    node: body,
8457                    scope: nested_scope,
8458                }));
8459            }
8460        }
8461        if declaration_node.kind() == "enum_specifier" {
8462            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8463        }
8464        code_unit
8465    }
8466
8467    fn visit_enum_enumerators(
8468        &mut self,
8469        ancestry: &ParentIndex<'_>,
8470        node: Node<'_>,
8471        scope: &ScopeInfo,
8472        parent: &CodeUnit,
8473    ) {
8474        walk_named_tree_preorder(node, false, |child| {
8475            if child.kind() != "enumerator" {
8476                return WalkControl::Continue;
8477            }
8478            let Some(name_node) = child.child_by_field_name("name") else {
8479                return WalkControl::Continue;
8480            };
8481            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8482            if name.is_empty() {
8483                return WalkControl::Continue;
8484            }
8485            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8486            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8487            WalkControl::Continue
8488        });
8489    }
8490
8491    fn visit_enum_enumerator(
8492        &mut self,
8493        ancestry: &ParentIndex<'_>,
8494        declaration_node: Node<'_>,
8495        scope: &ScopeInfo,
8496        parent: &CodeUnit,
8497        name: &str,
8498        signature: &str,
8499    ) {
8500        let code_unit = CodeUnit::new_fq(
8501            self.file.clone(),
8502            CodeUnitType::Field,
8503            scope.package_name.clone(),
8504            cpp_join_member_short(parent.short_name(), name),
8505            parent
8506                .fq()
8507                .clone()
8508                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8509        );
8510        if self.parsed.contains_declaration(&code_unit) {
8511            return;
8512        }
8513        self.add_declaration(
8514            ancestry,
8515            code_unit.clone(),
8516            declaration_node,
8517            Some(parent.clone()),
8518            None,
8519        );
8520        self.parsed.add_signature(code_unit, signature.to_string());
8521    }
8522
8523    fn visit_function_definition<'tree>(
8524        &mut self,
8525        node: Node<'tree>,
8526        scope: &ScopeInfo,
8527        stack: &mut Vec<CppWork<'tree>>,
8528        ancestry: &ParentIndex<'tree>,
8529    ) {
8530        // An attribute-like macro invocation whose argument is a declaration can
8531        // swallow every declaration written after it into one bogus
8532        // `function_definition` (#2551). This owns the whole region, so it runs
8533        // ahead of `visit_macro_swallowed_function_declarations` below, which
8534        // admits the same envelope by its head macro token and reads single
8535        // declarators out of nodes this recovery reparses properly.
8536        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8537            return;
8538        }
8539        // A file-scope object-like macro sentinel the parser cannot see (issue
8540        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8541        // prefixes as a bogus `function_definition` that swallows real namespaces,
8542        // classes, and members. Reparse the swallowed interior as C++ items so the
8543        // ordinary declaration visitors index it with byte/line-exact ownership.
8544        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8545            return;
8546        }
8547        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8548        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8549        // grammar folds the pair into the function's head as two leading
8550        // macro_modifiers where the older grammar left a damaged
8551        // field_declaration. Index the field; the function itself continues
8552        // through the ordinary path below.
8553        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8554            self.visit_omitted_semicolon_macro_field(
8555                node,
8556                type_modifier,
8557                name_modifier,
8558                scope,
8559                ancestry,
8560            );
8561        }
8562        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8563        // Layout() { ... }`): the same clean envelope, but the return type
8564        // names an active template type parameter, and the older grammar's
8565        // phantom field was the definition `OperandLayout::packed` resolved to
8566        // (#1129). Recover that field from the clean head.
8567        if scope.class_unit.is_some()
8568            && let Some((head_start, type_node)) =
8569                macro_return_template_parameter_field(node, self.source, ancestry)
8570        {
8571            self.visit_macro_return_template_parameter_field(
8572                node, head_start, type_node, scope, ancestry,
8573            );
8574        }
8575        if node.has_error() {
8576            self.visit_macro_swallowed_function_declarations(node, scope);
8577        }
8578        if let Some((class_node, name, raw_supertypes, base_facts)) =
8579            recover_exported_class_function_definition(node, self.source)
8580        {
8581            if let Some(body) = cpp_body_node(node)
8582                && let Some(close) = self
8583                    .orphaned_namespaces
8584                    .matching_close_brace(body.start_byte())
8585                && close.end_byte < body.end_byte()
8586            {
8587                let class_range = Range {
8588                    start_byte: node.start_byte(),
8589                    end_byte: close.end_byte,
8590                    start_line: node.start_position().row + 1,
8591                    end_line: close.end_line,
8592                };
8593                // Parse the body in class context, preserving the original
8594                // keyword, recovered name and body as included ranges. Parsing
8595                // the interior at file scope turns constructors and access
8596                // labels into another oversized ERROR instead of members.
8597                let mut head = vec![node];
8598                let mut keyword = None;
8599                let mut name_node = None;
8600                while let Some(part) = head.pop() {
8601                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8602                        continue;
8603                    }
8604                    if matches!(part.kind(), "class" | "struct" | "union") {
8605                        keyword = Some(part);
8606                    }
8607                    if matches!(part.kind(), "identifier" | "type_identifier")
8608                        && node_text(part, self.source) == name
8609                    {
8610                        name_node = Some(part);
8611                    }
8612                    let mut cursor = part.walk();
8613                    head.extend(part.children(&mut cursor));
8614                }
8615                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8616                    && let Some(type_name) = keyword
8617                        .parent()
8618                        .and_then(|parent| parent.child_by_field_name("name"))
8619                    && let Some(tree) = parse_source_ranges_with_cancellation(
8620                        &tree_sitter_cpp::LANGUAGE.into(),
8621                        self.source,
8622                        &[
8623                            (keyword.start_byte(), type_name.start_byte()),
8624                            (name_node.start_byte(), name_node.end_byte()),
8625                            (body.start_byte(), close.end_byte),
8626                        ],
8627                        None,
8628                    )
8629                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8630                    && let Some(class_body) = cpp_body_node(reparsed_class)
8631                    && class_body.start_byte() == body.start_byte()
8632                    && class_body.end_byte() == close.end_byte
8633                    && let Some(tail) =
8634                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8635                {
8636                    let class_body_range = class_body.byte_range();
8637                    self.with_recovery_source_context(node, |visitor| {
8638                        let source_context = visitor.active_source_context.clone();
8639                        let class_unit = visitor.visit_named_class_like_shape(
8640                            class_node,
8641                            name,
8642                            None,
8643                            true,
8644                            Some(class_range),
8645                            raw_supertypes,
8646                            base_facts,
8647                            scope,
8648                            stack,
8649                            ancestry,
8650                        );
8651                        visitor.parsed.record_materialization(
8652                            MaterializationRecord::RecoveredDeclaration {
8653                                recovery: class_range,
8654                                unit: class_unit.clone(),
8655                            },
8656                        );
8657                        let member_scope = ScopeInfo {
8658                            package_name: class_unit.package_name().to_string(),
8659                            class_unit: Some(class_unit),
8660                            declarations_are_fields: true,
8661                            template_metadata: None,
8662                            recovered_specialization_member_scope: false,
8663                            ..scope.clone()
8664                        };
8665                        let tail_range = tail.root_node().byte_range();
8666                        let class_body_recovery = cpp_recovery_window(
8667                            visitor.source,
8668                            class_body_range.start,
8669                            class_body_range.end,
8670                        );
8671                        let tail_recovery =
8672                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8673                        visitor.partitioned_regions.push(CppPartitionedRegion {
8674                            tree: tail,
8675                            range: tail_range,
8676                            scope: scope.clone(),
8677                            recovery: tail_recovery,
8678                            source_context: source_context.clone(),
8679                        });
8680                        visitor.partitioned_regions.push(CppPartitionedRegion {
8681                            tree,
8682                            range: class_body_range,
8683                            scope: member_scope,
8684                            recovery: class_body_recovery,
8685                            source_context: source_context.clone(),
8686                        });
8687                    });
8688                    return;
8689                }
8690            }
8691            let body = cpp_body_node(class_node);
8692            let displaced_namespace = cpp_body_node(node)
8693                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8694            let fragmented = cpp_body_node(node).and_then(|body| {
8695                fragmented_export_function_body_region(
8696                    node,
8697                    body,
8698                    self.source,
8699                    displaced_namespace.as_ref(),
8700                )
8701            });
8702            // The recovery tuple's first node is the class-like type when the
8703            // parser exposes one, but the synthetic wrapper owns the compound
8704            // statement that contains the truncated class body. Use the
8705            // wrapper body for fragmented-member detection; retain the
8706            // class-node body for the ordinary (non-fragmented) path below.
8707            if let Some(fragmented) = fragmented {
8708                // The lifted sibling no longer sits below the parser-visible
8709                // namespace node. Restore the current parent scope when the
8710                // ordinary work walk reaches that class.
8711                if let Some(boundary) =
8712                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8713                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8714                {
8715                    let mut boundary_scope = scope.clone();
8716                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8717                        if sibling.start_byte() >= boundary.start_byte() {
8718                            break;
8719                        }
8720                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8721                            boundary_scope.visible_using_namespaces.push(namespace);
8722                        }
8723                        push_cpp_using_declaration_binding(
8724                            &mut boundary_scope,
8725                            sibling,
8726                            self.source,
8727                        );
8728                    }
8729                    self.recovered_class_sibling_scopes
8730                        .insert(boundary.id(), boundary_scope);
8731                }
8732                let mut recovered_constructor = None;
8733                let mut recovered_prefix_tree = None;
8734                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8735                {
8736                    Some(FragmentedExportMembers::Complete(tree)) => {
8737                        if let Some(body) = body
8738                            && let Some(range) =
8739                                cpp_reparsed_synthetic_initializer_constructor_range(
8740                                    tree.root_node(),
8741                                    &name,
8742                                    self.source,
8743                                    body.end_byte(),
8744                                )
8745                        {
8746                            recovered_constructor = Some(range);
8747                            recovered_prefix_tree = Some(tree);
8748                            None
8749                        } else {
8750                            Some(FragmentedExportMembers::Complete(tree))
8751                        }
8752                    }
8753                    outcome => outcome,
8754                };
8755                let mut class_stack = Vec::new();
8756                let class_unit = self.visit_named_class_like_shape(
8757                    class_node,
8758                    name,
8759                    None,
8760                    true,
8761                    Some(fragmented.class_range),
8762                    raw_supertypes,
8763                    base_facts.clone(),
8764                    scope,
8765                    &mut class_stack,
8766                    ancestry,
8767                );
8768                self.parsed
8769                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8770                        recovery: fragmented.class_range,
8771                        unit: class_unit.clone(),
8772                    });
8773                let complete = outcome.is_some_and(|outcome| {
8774                    self.visit_fragmented_export_class_members(
8775                        node,
8776                        outcome,
8777                        class_unit.clone(),
8778                        scope,
8779                    )
8780                });
8781                if complete {
8782                    self.consumed_fragment_regions
8783                        .push((node.start_byte(), fragmented.class_range.end_byte));
8784                } else {
8785                    // The reparse can fail when the first constructor or a
8786                    // method body is split into statement-shaped siblings.
8787                    // Keep the recovered class envelope, but do not visit the
8788                    // synthetic wrapper body: its initializer expressions can
8789                    // look like same-named member functions (for example
8790                    // `Token.location(loc)`). Re-own only the original sibling
8791                    // nodes that fall inside the proven class range. Their CST
8792                    // shapes retain the real field/function kinds and ranges.
8793                    let member_scope = ScopeInfo {
8794                        package_name: class_unit.package_name().to_string(),
8795                        module: scope.module.clone(),
8796                        class_unit: Some(class_unit.clone()),
8797                        template_signature: scope.template_signature.clone(),
8798                        template_metadata: None,
8799                        declarations_are_fields: true,
8800                        recovered_specialization_member_scope: false,
8801                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8802                        local_block: None,
8803                        visible_using_declarations: scope.visible_using_declarations.clone(),
8804                    };
8805                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8806                        if candidate.start_byte() >= fragmented.reparse_end {
8807                            break;
8808                        }
8809                        if cpp_fragment_sibling_is_class_member(
8810                            candidate,
8811                            fragmented.reparse_end,
8812                            self.source,
8813                        ) {
8814                            self.recovered_class_sibling_scopes
8815                                .insert(candidate.id(), member_scope.clone());
8816                        }
8817                    }
8818                    if let Some(range) = recovered_constructor
8819                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8820                    {
8821                        let prefix_root = prefix_tree.root_node();
8822                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8823                        self.visit_recovered_fragment_prefix_members(
8824                            node,
8825                            prefix_root,
8826                            range.start,
8827                            &class_unit,
8828                            scope,
8829                            &prefix_ancestry,
8830                        );
8831                        self.visit_recovered_fragment_constructor(
8832                            range,
8833                            body,
8834                            class_node,
8835                            &class_unit,
8836                            scope,
8837                            ancestry,
8838                        );
8839                    }
8840                }
8841                if let Some(boundary) = displaced_namespace {
8842                    for item in boundary.namespace_items {
8843                        self.recovered_class_sibling_scopes
8844                            .insert(item.id(), scope.clone());
8845                    }
8846                }
8847                stack.extend(class_stack);
8848                return;
8849            }
8850            let mut stack = Vec::new();
8851            let class_unit = self.visit_named_class_like_shape(
8852                class_node,
8853                name,
8854                body,
8855                body.is_some(),
8856                None,
8857                raw_supertypes,
8858                base_facts,
8859                scope,
8860                &mut stack,
8861                ancestry,
8862            );
8863            self.parsed
8864                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8865                    recovery: cpp_declaration_range(node),
8866                    unit: class_unit,
8867                });
8868            // Issue #1524: the bogus `function_definition` body can run past
8869            // the class's true closing brace (the parse ends it with a
8870            // zero-width `MISSING "}"`), swallowing following namespace-scope
8871            // siblings -- they would index as members of the recovered class.
8872            // When the body's text-balanced close lands before the body's own
8873            // end, re-own the swallowed tail with the outer scope instead.
8874            if let Some(body) = body
8875                && let Some(class_close) = self
8876                    .orphaned_namespaces
8877                    .matching_close_brace(body.start_byte())
8878                && class_close.start_byte < body.end_byte()
8879            {
8880                let split = {
8881                    let mut cursor = body.walk();
8882                    body.named_children(&mut cursor)
8883                        .position(|child| child.start_byte() > class_close.start_byte)
8884                };
8885                if let Some(split) = split {
8886                    // The seeded work is a single Container over the whole
8887                    // body with the class scope; replace it with the bounded
8888                    // head (class scope) plus the swallowed tail (outer
8889                    // scope). Push tail first so the head drains first.
8890                    let seeded = stack.pop();
8891                    match seeded {
8892                        Some(CppWork::Container(container)) => {
8893                            push_cpp_sibling_range(
8894                                body,
8895                                split,
8896                                usize::MAX,
8897                                scope.clone(),
8898                                &mut stack,
8899                            );
8900                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
8901                        }
8902                        // visit_named_class_like_shape always seeds exactly
8903                        // one Container when a body is present.
8904                        _ => unreachable!("exported-class seed is always one Container"),
8905                    }
8906                }
8907            }
8908            while let Some(work) = stack.pop() {
8909                match work {
8910                    CppWork::Container(container) => {
8911                        push_cpp_container_work(container.node, container.scope, &mut stack);
8912                    }
8913                    CppWork::Siblings(siblings) => {
8914                        advance_cpp_siblings(siblings, self.source, &mut stack);
8915                    }
8916                    CppWork::Node(work) => {
8917                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
8918                    }
8919                }
8920            }
8921            return;
8922        }
8923        let recovered_constraint_constructor =
8924            cpp_recovered_template_macro_constructor(node, self.source);
8925        // A constraint-macro template head the parser split off keeps the
8926        // function's declared type parameter, so the member stays a template.
8927        let constrained_scope;
8928        let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
8929            Some(parameter) => {
8930                constrained_scope = ScopeInfo {
8931                    template_signature: Some(format!(
8932                        "<{}>",
8933                        normalize_cpp_whitespace(node_text(parameter, self.source))
8934                    )),
8935                    ..scope.clone()
8936                };
8937                &constrained_scope
8938            }
8939            None => scope,
8940        };
8941        let declarator = recovered_constraint_constructor
8942            .map(|(declarator, _)| declarator)
8943            .or_else(|| {
8944                scope.class_unit.as_ref().and_then(|class| {
8945                    recovered_macro_decorated_constructor_declarator(
8946                        node,
8947                        class.identifier(),
8948                        self.source,
8949                    )
8950                })
8951            })
8952            .or_else(|| node.child_by_field_name("declarator"));
8953        let Some(declarator) = declarator else {
8954            self.visit_malformed_function_definition_container(node, scope, stack);
8955            return;
8956        };
8957        let Some(function_declarator) = extract_function_declarator(declarator) else {
8958            self.visit_malformed_function_definition_container(node, scope, stack);
8959            return;
8960        };
8961        let function = if let Some((_, callable_name)) =
8962            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
8963        {
8964            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
8965        } else {
8966            extract_function_info(function_declarator, self.source, scope)
8967        };
8968        let Some(mut function) = function else {
8969            self.visit_malformed_function_definition_container(node, scope, stack);
8970            return;
8971        };
8972        if let Some((_, template_parameter)) = recovered_constraint_constructor {
8973            function.signature = format!(
8974                "template <{}>{}",
8975                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
8976                function.signature
8977            );
8978        }
8979        let code_unit = function.code_unit(self.file.clone());
8980        // Keep an earlier same-file prototype as another physical occurrence
8981        // of this callable. `CodeUnit` already identifies the role-neutral
8982        // overload, while ranges and signature metadata describe its
8983        // declaration/definition occurrences.
8984        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
8985        let signature = if recovered_constraint_constructor.is_some() {
8986            normalize_cpp_whitespace(node_text(function_declarator, self.source))
8987        } else {
8988            render_cpp_function_display_signature_from_node(
8989                node,
8990                self.source,
8991                scope.template_signature.as_deref(),
8992                true,
8993                ancestry,
8994            )
8995        };
8996        self.add_signature_with_metadata(
8997            code_unit.clone(),
8998            cpp_signature_metadata(
8999                signature,
9000                function_declarator,
9001                cpp_callable_linkage(node, self.source, ancestry),
9002                self.source,
9003                ancestry,
9004            )
9005            .with_declaration_only(false),
9006        );
9007        if let Some(parent) = &scope.class_unit {
9008            self.parsed.add_child(parent.clone(), code_unit.clone());
9009        } else if let Some(module) = &scope.module {
9010            self.parsed.add_child(module.clone(), code_unit.clone());
9011        }
9012        self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9013    }
9014
9015    /// Recover the namespace lost when tree-sitter promotes an export-macro
9016    /// class definition to a root-level `function_definition`.  Only a
9017    /// body-bearing, top-level recovery may borrow a namespace, and only when
9018    /// one earlier namespace-scope forward declaration proves the identity.
9019    fn scope_for_recovered_exported_class<'tree>(
9020        &mut self,
9021        node: Node<'tree>,
9022        name: &str,
9023        definition_body_present: bool,
9024        scope: &ScopeInfo,
9025        ancestry: &ParentIndex<'tree>,
9026    ) -> ScopeInfo {
9027        if !definition_body_present
9028            || !scope.package_name.is_empty()
9029            || scope.class_unit.is_some()
9030            || !(is_recovered_exported_class_container(node, self.source)
9031                || recover_function_like_export_class_pair(node, self.source).is_some()
9032                || recover_embedded_function_like_export_classes(node, self.source)
9033                    .iter()
9034                    .any(|recovered| recovered.name == name)
9035                || matches!(node.kind(), "declaration" | "field_declaration")
9036                    && recover_exported_class_declaration(node, self.source).is_some()
9037                || matches!(
9038                    node.kind(),
9039                    "class_specifier" | "struct_specifier" | "union_specifier"
9040                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9041                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9042                        name_node,
9043                        self.source,
9044                    )))
9045                }) || ancestry.parent(node).is_some_and(|parent| {
9046                    matches!(parent.kind(), "declaration" | "field_declaration")
9047                        && recover_exported_class_declaration(parent, self.source).is_some()
9048                        || is_recovered_exported_class_container(parent, self.source)
9049                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9050        {
9051            return scope.clone();
9052        }
9053        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9054        let Some(package_name) = borrowed_namespace
9055            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9056        else {
9057            return scope.clone();
9058        };
9059
9060        let module = CodeUnit::new_fq(
9061            self.file.clone(),
9062            CodeUnitType::Module,
9063            "",
9064            package_name.clone(),
9065            cpp_namespace_fq(&package_name),
9066        );
9067        let mut recovered = scope.clone();
9068        recovered.package_name = package_name;
9069        recovered.module = Some(module);
9070        recovered
9071    }
9072
9073    /// The unique namespace-scope forward declaration of `name` that precedes
9074    /// `recovered_node`, answered from the walk's carried-forward scan of the
9075    /// tree `recovered_node` belongs to.
9076    ///
9077    /// The scan is built on the first question and advanced by each later one,
9078    /// so a file that never reaches this path -- almost every file -- pays
9079    /// nothing, and one that reaches it thousands of times pays a single pass
9080    /// (#2754).
9081    fn unique_earlier_namespace_forward<'tree>(
9082        &mut self,
9083        recovered_node: Node<'tree>,
9084        name: &str,
9085        ancestry: &ParentIndex<'tree>,
9086    ) -> Option<String> {
9087        let mut root = recovered_node;
9088        while let Some(parent) = ancestry.parent(root) {
9089            root = parent;
9090        }
9091        let source = self.source;
9092        let scan = self
9093            .namespace_forward_scans
9094            .entry(CppTreeIdentity::of(root))
9095            .or_default();
9096        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9097        let borrowed = scan.unique_earlier_forward(name, recovered_node);
9098
9099        #[cfg(debug_assertions)]
9100        assert_eq!(
9101            borrowed,
9102            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9103            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9104             for {name} at byte {}",
9105            recovered_node.start_byte()
9106        );
9107
9108        borrowed
9109    }
9110
9111    fn visit_malformed_function_definition_container<'tree>(
9112        &mut self,
9113        node: Node<'tree>,
9114        scope: &ScopeInfo,
9115        stack: &mut Vec<CppWork<'tree>>,
9116    ) {
9117        let Some(body) = cpp_body_node(node) else {
9118            return;
9119        };
9120        if !cpp_contains_namespace_definition(body) {
9121            return;
9122        }
9123        stack.push(CppWork::Container(CppContainer {
9124            node: body,
9125            scope: scope.clone(),
9126        }));
9127    }
9128
9129    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
9130    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
9131    /// emits for a sentinel-prefixed region, reparse the interior after the
9132    /// sentinel identifier as real C++ items -- confined to the region so
9133    /// every reparsed node keeps its original byte/line position -- and run the
9134    /// ordinary container visitation over the result. Returns `true` when it fired
9135    /// (the caller must then skip normal function processing). Nested sentinel
9136    /// regions recover recursively: the reparsed interior is walked through the
9137    /// same `visit_function_definition` path, so a sentinel inside the region hits
9138    /// this recovery again.
9139    /// Runs `reparse_walk` and records every declaration it mints as a
9140    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
9141    /// `recovery` (issue #1657). A reparsed sentinel region has no single
9142    /// recovered envelope unit: the ordinary visitors mint namespaces,
9143    /// classes, and members directly from the reparsed tree, so the walk's
9144    /// declaration delta is the recovered set. Records are ordered by
9145    /// declaration start byte so the parse product stays deterministic.
9146    fn record_recovered_declarations(
9147        &mut self,
9148        recovery: Range,
9149        origin: Node<'_>,
9150        reparse_walk: impl FnOnce(&mut Self),
9151    ) {
9152        // The recovered declarations begin at the recovery start, which can lie
9153        // past a branch directive the malformed origin swallowed (#6325:
9154        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
9155        // the `#if` branch). Read their preprocessor context there.
9156        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9157            && recovery.start_byte < origin.end_byte())
9158        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9159        .flatten()
9160        .unwrap_or(origin);
9161        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9162            let source_context = visitor.active_source_context.clone();
9163            visitor.record_recovered_declarations_with_context(
9164                recovery,
9165                source_context,
9166                reparse_walk,
9167            );
9168        });
9169    }
9170
9171    /// Run a recovered walk with an owned source context and record every
9172    /// declaration it mints. Deferred partitioned regions cannot retain their
9173    /// origin `Node`, so they capture the context while the origin tree is
9174    /// live and use this same materialization path when drained.
9175    fn record_recovered_declarations_with_context(
9176        &mut self,
9177        recovery: Range,
9178        source_context: Option<CppActiveSourceContext>,
9179        reparse_walk: impl FnOnce(&mut Self),
9180    ) {
9181        // The set difference this used to be, kept as the oracle every answer
9182        // is asserted against (#2787).
9183        #[cfg(any(debug_assertions, test))]
9184        let before = self.parsed.declarations().clone();
9185
9186        self.recovery_captures.push(CppRecoveryCapture::default());
9187        let previous = self.active_source_context.take();
9188        self.active_source_context = source_context;
9189        reparse_walk(self);
9190        self.active_source_context = previous;
9191        let captured = self
9192            .recovery_captures
9193            .pop()
9194            .expect("the capture this call pushed is the one it pops");
9195
9196        // The capture holds every declaration created while it was open, once
9197        // each and in creation order, so the recovered set costs what the
9198        // recovery made rather than everything the file has declared so far.
9199        // One filter is left to apply: a created declaration that a later
9200        // deferred replacement removed is not in the parse product to report.
9201        let mut minted: Vec<CodeUnit> = captured
9202            .created
9203            .into_iter()
9204            .filter(|unit| self.parsed.contains_declaration(unit))
9205            .collect();
9206        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9207
9208        #[cfg(any(debug_assertions, test))]
9209        {
9210            let mut rediscovered: Vec<CodeUnit> = self
9211                .parsed
9212                .declarations()
9213                .iter()
9214                .filter(|unit| !before.contains(*unit))
9215                .cloned()
9216                .collect();
9217            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9218            assert_eq!(
9219                minted, rediscovered,
9220                "the captured recovered set must be the declaration delta of the reparse \
9221                 walk over {recovery:?}"
9222            );
9223        }
9224
9225        for unit in minted {
9226            self.parsed
9227                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9228                    recovery,
9229                    unit,
9230                });
9231        }
9232    }
9233
9234    /// Where one recovered declaration sorts: by start byte, then by name, so
9235    /// the parse product stays deterministic.
9236    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9237        let start = self
9238            .parsed
9239            .declaration_ranges(unit)
9240            .first()
9241            .map(|range| range.start_byte)
9242            .unwrap_or(usize::MAX);
9243        (start, unit.fq_name().to_string())
9244    }
9245
9246    fn visit_sentinel_macro_region<'tree>(
9247        &mut self,
9248        node: Node<'tree>,
9249        scope: &ScopeInfo,
9250        stack: &mut Vec<CppWork<'tree>>,
9251        ancestry: &ParentIndex<'tree>,
9252    ) -> bool {
9253        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9254            return true;
9255        }
9256        if let Some((
9257            reparse_start,
9258            class_start,
9259            body_start,
9260            class_close_start,
9261            class_close_end,
9262            class_close_line,
9263        )) = cpp_sentinel_macro_class_region(node, self.source)
9264        {
9265            let Some(class_tree) =
9266                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9267            else {
9268                return false;
9269            };
9270            let class_root = class_tree.root_node();
9271            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9272            // A region reparse is its own tree and needs its own parent index.
9273            let class_ancestry = self.recovery_ancestry(class_root);
9274            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9275                class_root,
9276                template_node,
9277                self.source,
9278                &class_ancestry,
9279            ) else {
9280                return false;
9281            };
9282            let class_node = reparsed_class.declaration_node;
9283            let name = reparsed_class.name;
9284            let mut class_scope = scope.clone();
9285            if let Some(template_node) = template_node {
9286                class_scope.template_signature =
9287                    cpp_template_signature(template_node, class_node, self.source);
9288                class_scope.template_metadata =
9289                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9290            }
9291            let Some(body_tree) =
9292                cpp_reparse_region_items(self.source, body_start, class_close_start)
9293            else {
9294                return false;
9295            };
9296            let raw_supertypes = reparsed_class.raw_supertypes;
9297            let base_facts = reparsed_class.base_facts;
9298            let class_range = Range {
9299                start_byte: class_start,
9300                end_byte: class_close_end,
9301                start_line: class_node.start_position().row + 1,
9302                end_line: class_close_line,
9303            };
9304            let class_scope = self.scope_for_recovered_exported_class(
9305                class_node,
9306                &name,
9307                true,
9308                &class_scope,
9309                &class_ancestry,
9310            );
9311            let mut class_stack = Vec::new();
9312            self.with_recovery_source_context(node, |visitor| {
9313                let class_unit = visitor.visit_named_class_like_shape(
9314                    class_node,
9315                    name,
9316                    None,
9317                    true,
9318                    Some(class_range),
9319                    raw_supertypes,
9320                    base_facts,
9321                    &class_scope,
9322                    &mut class_stack,
9323                    &class_ancestry,
9324                );
9325                visitor.parsed.record_materialization(
9326                    MaterializationRecord::RecoveredDeclaration {
9327                        recovery: class_range,
9328                        unit: class_unit.clone(),
9329                    },
9330                );
9331                let member_scope = ScopeInfo {
9332                    package_name: class_scope.package_name.clone(),
9333                    module: class_scope.module.clone(),
9334                    class_unit: Some(class_unit),
9335                    template_signature: class_scope.template_signature.clone(),
9336                    template_metadata: None,
9337                    declarations_are_fields: true,
9338                    recovered_specialization_member_scope: false,
9339                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9340                    local_block: None,
9341                    visible_using_declarations: class_scope.visible_using_declarations.clone(),
9342                };
9343                // The padded body reparse is its own tree, so it indexes itself.
9344                let body_root = body_tree.root_node();
9345                visitor.run_container_work(
9346                    body_root,
9347                    member_scope,
9348                    &visitor.recovery_ancestry(body_root),
9349                );
9350            });
9351            // Register only after the padded body reparse: its nodes deliberately
9352            // retain offsets inside the consumed region and must be visited first.
9353            self.consumed_fragment_regions
9354                .push((node.start_byte(), class_close_end));
9355            // An ERROR envelope can hold real sibling declarations after the
9356            // recovered class's close (the suffix-reparse boundary in
9357            // `cpp_sentinel_macro_class_region` partitions, it does not
9358            // consume). Walk the envelope's remaining children normally; the
9359            // consumed region above keeps the recovered class from being
9360            // indexed twice.
9361            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9362                stack.push(CppWork::Container(CppContainer {
9363                    node,
9364                    scope: scope.clone(),
9365                }));
9366            }
9367            return true;
9368        }
9369        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9370            return false;
9371        };
9372        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9373            return false;
9374        };
9375        let root = tree.root_node();
9376        if !cpp_reparsed_items_are_indexable(root, self.source) {
9377            return false;
9378        }
9379        let recovery = cpp_recovery_window(self.source, start, end);
9380        // The reparsed region is its own tree, so this walk indexes it itself.
9381        let reparsed_ancestry = self.recovery_ancestry(root);
9382        self.record_recovered_declarations(recovery, node, |visitor| {
9383            visitor.visit_container(
9384                root,
9385                &reparsed_ancestry,
9386                &scope.package_name,
9387                scope.module.clone(),
9388                scope.class_unit.clone(),
9389                scope.template_signature.clone(),
9390                scope.visible_using_namespaces.clone(),
9391                scope.visible_using_declarations.clone(),
9392            );
9393        });
9394        if end > node.end_byte() {
9395            self.consumed_fragment_regions
9396                .push((node.start_byte(), end));
9397        } else if node.kind() == "ERROR" && node.end_byte() > end {
9398            // The sentinel region ended at the first recovered class-like item
9399            // but the ERROR envelope keeps real sibling declarations after it
9400            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9401            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9402            // envelope's remaining children normally; the consumed region
9403            // keeps the reparsed prefix from being indexed twice.
9404            self.consumed_fragment_regions
9405                .push((node.start_byte(), end));
9406            stack.push(CppWork::Container(CppContainer {
9407                node,
9408                scope: scope.clone(),
9409            }));
9410        }
9411        true
9412    }
9413
9414    /// Re-own complete class declarations from the structured Abseil
9415    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9416    /// its direct CST children already prove both namespace components and the
9417    /// class bodies, so the ordinary class/member visitor can retain ownership
9418    /// and exact source ranges without admitting unrelated callable bodies.
9419    fn visit_nested_namespace_sentinel<'tree>(
9420        &mut self,
9421        node: Node<'tree>,
9422        scope: &ScopeInfo,
9423        ancestry: &ParentIndex<'tree>,
9424    ) -> bool {
9425        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9426            return false;
9427        };
9428        let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9429            recovered.function,
9430            &recovered.namespace_components,
9431            self.source,
9432        );
9433
9434        let mut package_name = scope.package_name.clone();
9435        let mut module = scope.module.clone();
9436        for component in recovered.namespace_components {
9437            package_name = if package_name.is_empty() {
9438                component
9439            } else {
9440                format!("{package_name}::{component}")
9441            };
9442            let namespace_module = CodeUnit::new_fq(
9443                self.file.clone(),
9444                CodeUnitType::Module,
9445                "",
9446                package_name.clone(),
9447                cpp_namespace_fq(&package_name),
9448            );
9449            if !self.parsed.contains_declaration(&namespace_module) {
9450                self.add_declaration(
9451                    ancestry,
9452                    namespace_module.clone(),
9453                    recovered.function,
9454                    None,
9455                    None,
9456                );
9457            }
9458            module = Some(namespace_module);
9459        }
9460
9461        let recovered_scope = ScopeInfo {
9462            package_name,
9463            module,
9464            // This scope's package comes from the namespace levels the sentinel
9465            // shape proves, so its owner must come from the same place. C++
9466            // never nests a namespace inside a class, so a surviving
9467            // `class_unit` here is recovery bleed from an earlier malformed
9468            // region, and keeping it would publish declarations whose package
9469            // names one lexical position and whose owner names another -- the
9470            // hybrid identity that trips the package/short boundary assert in
9471            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9472            // identity-neutral for valid code, where `class_unit` is always
9473            // empty at a namespace.
9474            class_unit: None,
9475            template_signature: scope.template_signature.clone(),
9476            template_metadata: scope.template_metadata.clone(),
9477            declarations_are_fields: false,
9478            recovered_specialization_member_scope: false,
9479            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9480            local_block: None,
9481            visible_using_declarations: scope.visible_using_declarations.clone(),
9482        };
9483        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9484            recovered.function,
9485            recovered.body,
9486            self.source,
9487            ancestry,
9488        ) {
9489            let mut class_scope = recovered_scope.clone();
9490            if let Some(template_node) = fragmented.template_node {
9491                class_scope.template_signature =
9492                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9493                class_scope.template_metadata = cpp_template_metadata(
9494                    template_node,
9495                    fragmented.class_node,
9496                    self.source,
9497                    ancestry,
9498                );
9499            }
9500            if let Some(outcome) = self
9501                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9502            {
9503                let mut class_stack = Vec::new();
9504                let class_unit = self.visit_named_class_like_shape(
9505                    fragmented.class_node,
9506                    fragmented.name.clone(),
9507                    None,
9508                    true,
9509                    Some(fragmented.fragmented.class_range),
9510                    fragmented.raw_supertypes.clone(),
9511                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9512                    &class_scope,
9513                    &mut class_stack,
9514                    ancestry,
9515                );
9516                self.parsed
9517                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9518                        recovery: fragmented.fragmented.class_range,
9519                        unit: class_unit.clone(),
9520                    });
9521                let mut class_lexical_scope = sentinel_namespace.clone();
9522                class_lexical_scope.push(fragmented.name.clone());
9523                self.visit_sentinel_class_aliases(
9524                    cpp_sentinel_fragmented_class_aliases(&fragmented),
9525                    &class_unit,
9526                    &class_scope,
9527                    &class_lexical_scope,
9528                    ancestry,
9529                );
9530                if self.visit_fragmented_export_class_members(
9531                    node,
9532                    outcome,
9533                    class_unit,
9534                    &class_scope,
9535                ) {
9536                    self.consumed_fragment_regions.push((
9537                        fragmented.consumed_start,
9538                        fragmented.fragmented.class_range.end_byte,
9539                    ));
9540                }
9541            }
9542        }
9543        // The class requirement above is the admission gate; once admitted,
9544        // traverse the whole proven inner namespace body so sibling aliases,
9545        // functions, and variables are not silently discarded. The body is a
9546        // node of the tree being walked, so it reuses that tree's index.
9547        self.run_container_work(recovered.body, recovered_scope, ancestry);
9548        true
9549    }
9550
9551    /// Publish the original structured aliases of a recovered sentinel class
9552    /// as its members, then claim their byte ranges. The claim keeps the member
9553    /// reparse from publishing its statement-context misreading of the same
9554    /// text, and keeps the outer walk from publishing the alias again at
9555    /// namespace scope when the reparse leaves the class region unclaimed.
9556    ///
9557    /// The malformed CST ancestry names only the namespaces outside the
9558    /// sentinel, so the target type is read in `lexical_scope`, the recovered
9559    /// class scope the usage extractor also assigns to these nodes.
9560    fn visit_sentinel_class_aliases<'tree>(
9561        &mut self,
9562        aliases: Vec<Node<'tree>>,
9563        class_unit: &CodeUnit,
9564        scope: &ScopeInfo,
9565        lexical_scope: &[String],
9566        ancestry: &ParentIndex<'tree>,
9567    ) {
9568        let member_scope = ScopeInfo {
9569            package_name: class_unit.package_name().to_string(),
9570            module: scope.module.clone(),
9571            class_unit: Some(class_unit.clone()),
9572            template_signature: scope.template_signature.clone(),
9573            template_metadata: None,
9574            declarations_are_fields: true,
9575            recovered_specialization_member_scope: false,
9576            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9577            local_block: None,
9578            visible_using_declarations: scope.visible_using_declarations.clone(),
9579        };
9580        for alias in aliases {
9581            let mut alias_scope = member_scope.clone();
9582            let declaration = if alias.kind() == "template_declaration" {
9583                let declaration = alias
9584                    .named_children(&mut alias.walk())
9585                    .find(|child| child.kind() == "alias_declaration")
9586                    .expect("an admitted template alias holds its alias declaration");
9587                alias_scope.template_signature =
9588                    cpp_template_signature(alias, declaration, self.source);
9589                alias_scope.template_metadata =
9590                    cpp_template_metadata(alias, declaration, self.source, ancestry);
9591                declaration
9592            } else {
9593                alias
9594            };
9595            self.consumed_fragment_regions
9596                .push((alias.start_byte(), alias.end_byte()));
9597            let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9598                continue;
9599            };
9600            let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9601            let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9602            self.record_type_aliases(
9603                ancestry,
9604                declaration,
9605                &alias_scope,
9606                vec![name.clone()],
9607                signature,
9608                cpp_declaration_range(declaration),
9609                lexical_scope,
9610            );
9611            // The source fact reads its lexical path from the same malformed
9612            // ancestry; structural alias navigation joins this path.
9613            if let Some(declaration) = self
9614                .source_links
9615                .get(&unit)
9616                .and_then(|declarations| declarations.last())
9617            {
9618                let mut properties = self.source_properties.borrow_mut();
9619                let fact = properties
9620                    .get_mut(declaration)
9621                    .expect("a linked alias has a source fact");
9622                fact.lexical_path = lexical_scope.to_vec();
9623                fact.lexical_path.push(name);
9624            }
9625        }
9626    }
9627
9628    fn visit_declaration<'tree>(
9629        &mut self,
9630        node: Node<'tree>,
9631        scope: &ScopeInfo,
9632        in_class_body: bool,
9633        stack: &mut Vec<CppWork<'tree>>,
9634        ancestry: &ParentIndex<'tree>,
9635    ) {
9636        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9637            return;
9638        }
9639        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9640            return;
9641        }
9642        // Statement-context parsing treats Ctor(int value) as a parenthesized
9643        // variable declarator. Validate it as a class member before publishing
9644        // its parameter name as a field.
9645        if in_class_body
9646            && scope.class_unit.as_ref().is_some_and(|owner| {
9647                node.child_by_field_name("type").is_some_and(|ty| {
9648                    ty.kind() == "type_identifier"
9649                        && node_text(ty, self.source) == owner.identifier()
9650                })
9651            })
9652            && node
9653                .child_by_field_name("declarator")
9654                .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9655            && self.visit_bare_constructor_expression(node, scope, ancestry)
9656        {
9657            return;
9658        }
9659        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9660            !cpp_active_template_type_parameter(
9661                node,
9662                node_text(declarator, self.source),
9663                self.source,
9664                ancestry,
9665            )
9666        }) {
9667            return;
9668        }
9669        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9670            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9671            // the following `Imaging image` member into one malformed field.
9672            // The ERROR's identifier is the actual declarator; route it
9673            // through the ordinary field path so its parent, range, and
9674            // signature metadata remain consistent with every other member.
9675            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9676            return;
9677        }
9678        if in_class_body
9679            && let Some(parent) = scope.class_unit.as_ref()
9680            && let Some(call) =
9681                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9682        {
9683            self.visit_recovered_macro_qualified_constructor_definition(
9684                node, call, scope, ancestry,
9685            );
9686            return;
9687        }
9688        if in_class_body
9689            && let Some(parent) = scope.class_unit.as_ref()
9690            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9691                node,
9692                parent.identifier(),
9693                self.source,
9694            )
9695        {
9696            self.visit_recovered_macro_initialized_constructor_definition(
9697                node,
9698                constructor,
9699                scope,
9700                ancestry,
9701            );
9702            return;
9703        }
9704        if in_class_body
9705            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9706        {
9707            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9708            return;
9709        }
9710        if in_class_body
9711            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9712        {
9713            for member in members {
9714                self.add_macro_wrapped_declaration(member, scope, ancestry);
9715            }
9716            return;
9717        }
9718        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9719            return;
9720        }
9721        if in_class_body
9722            && let Some(declarators) =
9723                recovered_macro_qualified_field_declarators(node, self.source)
9724        {
9725            for declarator in declarators {
9726                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9727            }
9728            return;
9729        }
9730        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9731        if !recovered_alias_names.is_empty() {
9732            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9733            return;
9734        }
9735        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9736            return;
9737        }
9738        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9739            return;
9740        }
9741
9742        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9743            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9744                // Issue #938: the members tree-sitter scattered out of the fragmented
9745                // multiple-base export node are reparsed from their true body region
9746                // and re-owned as members of the recovered class, with an explicit
9747                // navigation range spanning to the displaced closing brace.
9748                if let Some(outcome) =
9749                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9750                {
9751                    let consumed_region = (
9752                        recovered.declaration_node.end_byte(),
9753                        fragmented.class_range.end_byte,
9754                    );
9755                    let code_unit = self.visit_named_class_like_shape(
9756                        recovered.declaration_node,
9757                        recovered.name,
9758                        None,
9759                        true,
9760                        Some(fragmented.class_range),
9761                        recovered.raw_supertypes,
9762                        recovered.base_facts,
9763                        scope,
9764                        stack,
9765                        ancestry,
9766                    );
9767                    self.parsed.record_materialization(
9768                        MaterializationRecord::RecoveredDeclaration {
9769                            recovery: fragmented.class_range,
9770                            unit: code_unit.clone(),
9771                        },
9772                    );
9773                    let consume_fragment =
9774                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9775                    // Everything between the fragmented declaration and its displaced
9776                    // closing brace now belongs to the recovered class; keep the
9777                    // ordinary walk from re-indexing those scattered siblings at top
9778                    // level. Register the consumed region only after indexing because
9779                    // the reparsed nodes retain byte offsets inside that same region.
9780                    if consume_fragment {
9781                        self.consumed_fragment_regions.push(consumed_region);
9782                    }
9783                    return;
9784                }
9785            }
9786            let uses_initializer_body = recovered.uses_initializer_body;
9787            let definition_body_present = recovered.body.is_some();
9788            let class_unit = self.visit_named_class_like_shape(
9789                recovered.declaration_node,
9790                recovered.name,
9791                recovered.body,
9792                definition_body_present,
9793                None,
9794                recovered.raw_supertypes,
9795                recovered.base_facts,
9796                scope,
9797                stack,
9798                ancestry,
9799            );
9800            self.parsed
9801                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9802                    recovery: cpp_declaration_range(node),
9803                    unit: class_unit,
9804                });
9805            if uses_initializer_body {
9806                return;
9807            }
9808        }
9809
9810        // A function-like macro invocation that expands to declarations keeps
9811        // no terminator of its own: the semicolons live in the replacement
9812        // body, which is not part of this tree. Tree-sitter recovers one and
9813        // reads the macro name as the member type and its argument list as a
9814        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9815        // published a field named `ClosedDetect` beside the real constructor
9816        // and split every reference to the constructor's name between the two
9817        // (#3298). The invented terminator is the whole difference from a real
9818        // member: a member terminates itself, and with a terminator the
9819        // grammar reads `T (name);` as a declaration rather than a field.
9820        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9821            return;
9822        }
9823
9824        let mut handled_function = false;
9825        let mut handled_declarator = false;
9826        let mut cursor = node.walk();
9827        for child in node.named_children(&mut cursor) {
9828            if matches!(
9829                child.kind(),
9830                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9831            ) {
9832                // A named class-like definition remains a declaration even when
9833                // the same statement also declares an object, for example
9834                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9835                // declaration's type and `kind` as its declarator.  Dropping the
9836                // type here loses both its nested owner and every later lexical
9837                // reference to it.  A body is the structured proof that this is
9838                // a definition rather than an elaborated type use such as
9839                // `class Kind value;`.
9840                if cpp_body_node(child).is_some() {
9841                    self.visit_class_like(child, scope, stack, ancestry);
9842                }
9843                continue;
9844            }
9845        }
9846
9847        let mut cursor = node.walk();
9848        for child in node.children_by_field_name("declarator", &mut cursor) {
9849            if crate::structural::is_recovered_designator_init_declarator(child) {
9850                handled_declarator = true;
9851                continue;
9852            }
9853            if in_class_body
9854                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9855            {
9856                handled_declarator = true;
9857                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9858                continue;
9859            }
9860            if let Some(kind) = classify_declarator(child) {
9861                handled_declarator = true;
9862                match kind {
9863                    DeclaratorKind::Function(function_declarator) => {
9864                        handled_function = true;
9865                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9866                    }
9867                    DeclaratorKind::Variable(variable_declarator) => {
9868                        self.visit_variable_declaration(
9869                            node,
9870                            variable_declarator,
9871                            scope,
9872                            in_class_body,
9873                            ancestry,
9874                        );
9875                    }
9876                }
9877            }
9878        }
9879
9880        if !handled_declarator {
9881            let mut cursor = node.walk();
9882            for child in node.named_children(&mut cursor) {
9883                if crate::structural::is_recovered_designator_init_declarator(child) {
9884                    handled_declarator = true;
9885                    continue;
9886                }
9887                if !is_unfielded_declarator_candidate(child) {
9888                    continue;
9889                }
9890                let Some(kind) = classify_declarator(child) else {
9891                    continue;
9892                };
9893                handled_declarator = true;
9894                match kind {
9895                    DeclaratorKind::Function(function_declarator) => {
9896                        handled_function = true;
9897                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9898                    }
9899                    DeclaratorKind::Variable(variable_declarator) => {
9900                        self.visit_variable_declaration(
9901                            node,
9902                            variable_declarator,
9903                            scope,
9904                            in_class_body,
9905                            ancestry,
9906                        );
9907                    }
9908                }
9909            }
9910        }
9911
9912        if handled_function {
9913            return;
9914        }
9915
9916        if !handled_declarator {
9917            if in_class_body {
9918                self.visit_class_members_from_declaration(node, scope, ancestry);
9919            } else {
9920                self.visit_global_variables_from_declaration(node, scope, ancestry);
9921            }
9922        }
9923    }
9924
9925    /// Preserve the member structure of an anonymous aggregate.
9926    ///
9927    /// An anonymous union with no declarator promotes its fields into the
9928    /// containing aggregate. An anonymous struct/union followed by a named
9929    /// declarator, such as `struct { T *ops; } sock`, declares both the field
9930    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
9931    /// the declarator's structured nested identity so a later `value.sock.ops`
9932    /// chain can traverse it without parsing a type spelling (#2407).
9933    ///
9934    /// An anonymous aggregate declares no tag, so there is nothing for the C
9935    /// dialect to re-scope and nothing for the C++ dialect to nest: both
9936    /// readings of this shape are the same, and both need these members
9937    /// (#3301).
9938    fn anonymous_aggregate_declaration_parts<'tree>(
9939        node: Node<'tree>,
9940    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
9941        let aggregate = node.child_by_field_name("type")?;
9942        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
9943            || aggregate.child_by_field_name("name").is_some()
9944        {
9945            return None;
9946        }
9947        let body = cpp_body_node(aggregate)?;
9948        let mut cursor = node.walk();
9949        let declarators = node
9950            .children_by_field_name("declarator", &mut cursor)
9951            .filter_map(|declarator| match classify_declarator(declarator) {
9952                Some(DeclaratorKind::Variable(variable)) => Some(variable),
9953                Some(DeclaratorKind::Function(_)) | None => None,
9954            })
9955            .collect();
9956        Some((aggregate, body, declarators))
9957    }
9958
9959    fn visit_anonymous_aggregate_declaration<'tree>(
9960        &mut self,
9961        node: Node<'tree>,
9962        scope: &ScopeInfo,
9963        in_class_body: bool,
9964        stack: &mut Vec<CppWork<'tree>>,
9965        ancestry: &ParentIndex<'tree>,
9966    ) -> bool {
9967        if !in_class_body || scope.class_unit.is_none() {
9968            return false;
9969        }
9970        let Some((aggregate, body, declarators)) =
9971            Self::anonymous_aggregate_declaration_parts(node)
9972        else {
9973            return false;
9974        };
9975        if declarators.is_empty() {
9976            stack.push(CppWork::Container(CppContainer {
9977                node: body,
9978                scope: scope.clone(),
9979            }));
9980            return true;
9981        }
9982
9983        for declarator in declarators {
9984            let Some(name) = extract_variable_name(declarator, self.source) else {
9985                continue;
9986            };
9987            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9988            // The receiver type's identity is the declarator's name, so its
9989            // range is the field declaration that carries that name token.
9990            // The inner specifier ends at the closing brace, which leaves the
9991            // name the unit is known by outside its own text (#3287).
9992            self.visit_named_class_like_shape(
9993                aggregate,
9994                name,
9995                Some(body),
9996                true,
9997                Some(cpp_declaration_range(node)),
9998                None,
9999                None,
10000                scope,
10001                stack,
10002                ancestry,
10003            );
10004        }
10005        true
10006    }
10007
10008    /// Give a function-local anonymous aggregate a structured owner so its
10009    /// direct pointer bindings can be typed by the usage graph.  Unlike an
10010    /// anonymous aggregate in a class body, there is no source-level tag or
10011    /// typedef to supply an identity.  The aggregate's CST range is therefore
10012    /// part of a generated, collision-resistant identity; no source spelling
10013    /// is consulted.  The declaration's own variable remains a file-level
10014    /// field projection, while the aggregate body is visited as the generated
10015    /// class's field list.
10016    fn visit_anonymous_local_aggregate_declaration<'tree>(
10017        &mut self,
10018        node: Node<'tree>,
10019        scope: &ScopeInfo,
10020        stack: &mut Vec<CppWork<'tree>>,
10021        ancestry: &ParentIndex<'tree>,
10022    ) -> bool {
10023        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10024            return false;
10025        }
10026        let Some((aggregate, body, declarators)) =
10027            Self::anonymous_aggregate_declaration_parts(node)
10028        else {
10029            return false;
10030        };
10031        if declarators.is_empty() {
10032            return false;
10033        }
10034
10035        for declarator in &declarators {
10036            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10037        }
10038        let name = format!("<anonymous:{}>", aggregate.start_byte());
10039        self.visit_named_class_like_shape(
10040            aggregate,
10041            name,
10042            Some(body),
10043            true,
10044            None,
10045            None,
10046            None,
10047            scope,
10048            stack,
10049            ancestry,
10050        );
10051        true
10052    }
10053
10054    fn visit_function_declaration<'tree>(
10055        &mut self,
10056        declaration_node: Node<'tree>,
10057        declarator: Node<'tree>,
10058        scope: &ScopeInfo,
10059        ancestry: &ParentIndex<'tree>,
10060    ) {
10061        let Some(function) = extract_function_info(declarator, self.source, scope) else {
10062            return;
10063        };
10064        // An in-class member prototype is a real source declaration, not a
10065        // generated or recovery-only unit, so it keeps the ordinary code-unit
10066        // identity (#3508). #3493 recorded its callable modifiers, which is
10067        // what a canonical procedure key needs; a synthetic unit would still
10068        // refuse that key on identity grounds alone.
10069        let code_unit = function.code_unit(self.file.clone());
10070        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10071            return;
10072        }
10073        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10074        let signature = render_cpp_function_display_signature_from_node(
10075            declaration_node,
10076            self.source,
10077            scope.template_signature.as_deref(),
10078            false,
10079            ancestry,
10080        );
10081        self.add_signature_with_metadata(
10082            code_unit.clone(),
10083            cpp_signature_metadata(
10084                signature,
10085                declarator,
10086                cpp_callable_linkage(declaration_node, self.source, ancestry),
10087                self.source,
10088                ancestry,
10089            )
10090            .with_declaration_only(true),
10091        );
10092        if let Some(parent) = &scope.class_unit {
10093            self.parsed.add_child(parent.clone(), code_unit);
10094        } else if let Some(module) = &scope.module {
10095            self.parsed.add_child(module.clone(), code_unit);
10096        }
10097    }
10098
10099    fn link_existing_source_declaration(
10100        &mut self,
10101        ancestry: &ParentIndex<'_>,
10102        code_unit: &CodeUnit,
10103        declaration_node: Node<'_>,
10104    ) -> bool {
10105        if !self.parsed.contains_declaration(code_unit) {
10106            return false;
10107        }
10108        self.link_source_declaration(ancestry, code_unit, declaration_node);
10109        self.parsed
10110            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10111        true
10112    }
10113
10114    fn visit_recovered_macro_qualified_function_declaration<'tree>(
10115        &mut self,
10116        declaration_node: Node<'tree>,
10117        call: Node<'tree>,
10118        scope: &ScopeInfo,
10119        ancestry: &ParentIndex<'tree>,
10120    ) {
10121        let Some(parent) = &scope.class_unit else {
10122            return;
10123        };
10124        let Some(name_node) = call.child_by_field_name("function") else {
10125            return;
10126        };
10127        let Some(arguments) = call.child_by_field_name("arguments") else {
10128            return;
10129        };
10130        let Some((signature, parameter_labels, discriminator)) =
10131            recovered_macro_qualified_function_parameters(arguments, self.source)
10132        else {
10133            return;
10134        };
10135        let arity = parameter_labels.len();
10136        let function = FunctionInfo {
10137            package_name: scope.package_name.clone(),
10138            owner: Some(CppMemberOwner::Unit(parent.clone())),
10139            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10140            signature,
10141        };
10142        if function.name.is_empty() {
10143            return;
10144        }
10145        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10146        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10147            return;
10148        }
10149        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10150        let signature_label = render_cpp_function_display_signature_from_node(
10151            declaration_node,
10152            self.source,
10153            scope.template_signature.as_deref(),
10154            false,
10155            ancestry,
10156        );
10157        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10158        // Only the callable's parameters came from the reparse: the
10159        // `field_declaration` this recovery reads is a real node in a real
10160        // class body, so its storage class and the body's access ladder state
10161        // this member's modifiers exactly as an ordinary member's do.
10162        let modifiers =
10163            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10164        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10165            .with_declaration_only(true)
10166            .with_callable_arity(CallableArity::exact(arity))
10167            .with_overload_discriminator(Some(discriminator))
10168            .with_callable_linkage(linkage)
10169            .with_callable_modifiers(
10170                modifiers.is_static,
10171                modifiers.is_constructor,
10172                modifiers.visibility,
10173            );
10174        self.add_signature_with_metadata(code_unit.clone(), metadata);
10175        self.parsed.add_child(parent.clone(), code_unit);
10176    }
10177
10178    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10179        &mut self,
10180        declaration_node: Node<'tree>,
10181        call: Node<'tree>,
10182        scope: &ScopeInfo,
10183        ancestry: &ParentIndex<'tree>,
10184    ) {
10185        let Some(parent) = &scope.class_unit else {
10186            return;
10187        };
10188        let Some(arguments) = call.child_by_field_name("arguments") else {
10189            return;
10190        };
10191        let Some((mut signature, parameter_labels, discriminator)) =
10192            recovered_macro_qualified_function_parameters(arguments, self.source)
10193        else {
10194            return;
10195        };
10196        if let Some(template_signature) = &scope.template_signature {
10197            signature = format!("{template_signature}{signature}");
10198        }
10199        let arity = parameter_labels.len();
10200        let function = FunctionInfo {
10201            package_name: scope.package_name.clone(),
10202            owner: Some(CppMemberOwner::Unit(parent.clone())),
10203            name: parent.identifier().to_string(),
10204            signature,
10205        };
10206        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10207        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10208        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10209        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10210        // The recovery already proved this member is the class's constructor:
10211        // the reparsed call names the enclosing class. Everything else is read
10212        // from the real `field_declaration` the recovery found it in.
10213        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10214        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10215            .with_declaration_only(false)
10216            .with_callable_arity(CallableArity::exact(arity))
10217            .with_overload_discriminator(Some(discriminator))
10218            .with_callable_linkage(linkage)
10219            .with_callable_modifiers(
10220                modifiers.is_static,
10221                modifiers.is_constructor,
10222                modifiers.visibility,
10223            );
10224        self.add_signature_with_metadata(code_unit.clone(), metadata);
10225        self.parsed.add_child(parent.clone(), code_unit);
10226    }
10227
10228    /// Publish the constructor of the macro-decorated field shape that
10229    /// `recovered_macro_initialized_constructor_declarator` proves. The head
10230    /// is a real, clean function declarator, so it keeps the ordinary
10231    /// identity and metadata; the `default_value` initializer list is the
10232    /// constructor body, so the occurrence is a definition.
10233    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10234        &mut self,
10235        declaration_node: Node<'tree>,
10236        function_declarator: Node<'tree>,
10237        scope: &ScopeInfo,
10238        ancestry: &ParentIndex<'tree>,
10239    ) {
10240        let Some(parent) = &scope.class_unit else {
10241            return;
10242        };
10243        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10244            return;
10245        };
10246        let code_unit = function.code_unit(self.file.clone());
10247        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10248        // Render the head the way an ordinary constructor definition renders
10249        // the text ahead of its body, member initializers included.
10250        let body_start = declaration_node
10251            .child_by_field_name("default_value")
10252            .map_or(declaration_node.end_byte(), |body| body.start_byte());
10253        let head =
10254            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10255        let signature = match scope.template_signature.as_deref() {
10256            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10257            None => format!("{head} {{...}}"),
10258        };
10259        self.add_signature_with_metadata(
10260            code_unit.clone(),
10261            cpp_signature_metadata(
10262                signature,
10263                function_declarator,
10264                cpp_callable_linkage(declaration_node, self.source, ancestry),
10265                self.source,
10266                ancestry,
10267            )
10268            .with_declaration_only(false),
10269        );
10270        self.parsed.add_child(parent.clone(), code_unit);
10271    }
10272
10273    fn visit_variable_declaration<'tree>(
10274        &mut self,
10275        declaration_node: Node<'tree>,
10276        declarator: Node<'tree>,
10277        scope: &ScopeInfo,
10278        in_class_body: bool,
10279        ancestry: &ParentIndex<'tree>,
10280    ) {
10281        let Some(name) = extract_variable_name(declarator, self.source) else {
10282            return;
10283        };
10284        let parent = if in_class_body {
10285            let Some(parent) = &scope.class_unit else {
10286                return;
10287            };
10288            Some(parent)
10289        } else {
10290            None
10291        };
10292        let short_name = match parent {
10293            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10294            None => name.clone(),
10295        };
10296        let fq = cpp_leaf_fq(
10297            &scope.package_name,
10298            parent,
10299            &name,
10300            SegmentKind::Member,
10301            SegmentKind::Member,
10302        );
10303        let code_unit = CodeUnit::new_fq(
10304            self.file.clone(),
10305            CodeUnitType::Field,
10306            scope.package_name.clone(),
10307            short_name,
10308            fq,
10309        );
10310        if self.parsed.contains_declaration(&code_unit) {
10311            return;
10312        }
10313        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10314        self.add_signature_with_metadata(
10315            code_unit.clone(),
10316            SignatureMetadata::new(
10317                render_cpp_field_signature(declaration_node, declarator, self.source),
10318                Vec::new(),
10319            )
10320            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10321                declaration_node,
10322                self.source,
10323                ancestry,
10324            )),
10325        );
10326        if let Some(parent) = &scope.class_unit {
10327            self.parsed.add_child(parent.clone(), code_unit);
10328        } else if let Some(module) = &scope.module {
10329            self.parsed.add_child(module.clone(), code_unit);
10330        }
10331    }
10332
10333    /// Index the field half of an omitted-semicolon pair the macro-declaration
10334    /// grammar folded into the following function definition's head. The
10335    /// function itself continues through the ordinary visitor path.
10336    fn visit_omitted_semicolon_macro_field<'tree>(
10337        &mut self,
10338        node: Node<'tree>,
10339        type_modifier: Node<'tree>,
10340        name_modifier: Node<'tree>,
10341        scope: &ScopeInfo,
10342        ancestry: &ParentIndex<'tree>,
10343    ) {
10344        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10345            return;
10346        };
10347        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10348        if name.is_empty() {
10349            return;
10350        }
10351        let range = Range {
10352            start_byte: type_modifier.start_byte(),
10353            end_byte: name_modifier.end_byte(),
10354            start_line: type_modifier.start_position().row + 1,
10355            end_line: name_modifier.end_position().row + 1,
10356        };
10357        self.record_macro_head_field(node, range, name, scope, ancestry);
10358    }
10359
10360    /// Index the macro-return stand-in field: a clean function definition whose
10361    /// macro-modified head returns an active template type parameter. The field
10362    /// is named after the return type and spans the head up to it, exactly the
10363    /// phantom `field_declaration` the older grammar emitted (#1129).
10364    fn visit_macro_return_template_parameter_field<'tree>(
10365        &mut self,
10366        node: Node<'tree>,
10367        head_start: Node<'tree>,
10368        type_node: Node<'tree>,
10369        scope: &ScopeInfo,
10370        ancestry: &ParentIndex<'tree>,
10371    ) {
10372        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10373        let range = Range {
10374            start_byte: head_start.start_byte(),
10375            end_byte: type_node.end_byte(),
10376            start_line: head_start.start_position().row + 1,
10377            end_line: type_node.end_position().row + 1,
10378        };
10379        self.record_macro_head_field(node, range, name, scope, ancestry);
10380    }
10381
10382    /// Record one field recovered from a function definition's macro head. The
10383    /// signature is the head's own text: like the older grammar's phantom
10384    /// field_declaration, it carries no semicolon because none was written.
10385    fn record_macro_head_field<'tree>(
10386        &mut self,
10387        node: Node<'tree>,
10388        range: Range,
10389        name: String,
10390        scope: &ScopeInfo,
10391        ancestry: &ParentIndex<'tree>,
10392    ) {
10393        if name.is_empty() {
10394            return;
10395        }
10396        let parent = scope.class_unit.as_ref();
10397        let short_name = match parent {
10398            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10399            None => name.clone(),
10400        };
10401        let code_unit = CodeUnit::new_fq(
10402            self.file.clone(),
10403            CodeUnitType::Field,
10404            scope.package_name.clone(),
10405            short_name,
10406            cpp_leaf_fq(
10407                &scope.package_name,
10408                parent,
10409                &name,
10410                SegmentKind::Member,
10411                SegmentKind::Member,
10412            ),
10413        );
10414        if self.parsed.contains_declaration(&code_unit) {
10415            return;
10416        }
10417        self.add_declaration_with_range(
10418            ancestry,
10419            code_unit.clone(),
10420            range,
10421            Some(node),
10422            parent.cloned(),
10423            None,
10424        );
10425        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10426        self.add_signature_with_metadata(
10427            code_unit.clone(),
10428            SignatureMetadata::new(signature, Vec::new())
10429                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10430        );
10431        if let Some(parent) = &scope.class_unit {
10432            self.parsed.add_child(parent.clone(), code_unit);
10433        } else if let Some(module) = &scope.module {
10434            self.parsed.add_child(module.clone(), code_unit);
10435        }
10436    }
10437
10438    fn variable_declarator_from_declaration_child<'tree>(
10439        child: Node<'tree>,
10440    ) -> Option<Node<'tree>> {
10441        if child.kind() == "init_declarator" {
10442            child.child_by_field_name("declarator")
10443        } else if matches!(
10444            child.kind(),
10445            "identifier"
10446                | "field_identifier"
10447                | "pointer_declarator"
10448                | "reference_declarator"
10449                | "array_declarator"
10450                | "parenthesized_declarator"
10451                | "macro_modifier_declarator"
10452        ) {
10453            Some(child)
10454        } else {
10455            None
10456        }
10457    }
10458
10459    fn visit_class_members_from_declaration<'tree>(
10460        &mut self,
10461        node: Node<'tree>,
10462        scope: &ScopeInfo,
10463        ancestry: &ParentIndex<'tree>,
10464    ) {
10465        let mut cursor = node.walk();
10466        for child in node.named_children(&mut cursor) {
10467            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10468                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10469            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10470            {
10471                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10472            }
10473        }
10474    }
10475
10476    fn visit_global_variables_from_declaration<'tree>(
10477        &mut self,
10478        node: Node<'tree>,
10479        scope: &ScopeInfo,
10480        ancestry: &ParentIndex<'tree>,
10481    ) {
10482        let mut cursor = node.walk();
10483        for child in node.named_children(&mut cursor) {
10484            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10485                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10486            }
10487        }
10488    }
10489
10490    fn visit_type_declaration<'tree>(
10491        &mut self,
10492        node: Node<'tree>,
10493        scope: &ScopeInfo,
10494        stack: &mut Vec<CppWork<'tree>>,
10495        ancestry: &ParentIndex<'tree>,
10496    ) {
10497        let type_node = node.child_by_field_name("type");
10498        if let Some(type_node) = type_node
10499            && matches!(
10500                type_node.kind(),
10501                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10502            )
10503        {
10504            self.visit_class_like(type_node, scope, stack, ancestry);
10505        }
10506
10507        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10508            let range = Range {
10509                start_byte: node.start_byte(),
10510                end_byte: recovered.end_node.end_byte(),
10511                start_line: node.start_position().row + 1,
10512                end_line: recovered.end_node.end_position().row + 1,
10513            };
10514            let signature = self
10515                .source
10516                .get(range.start_byte..range.end_byte)
10517                .map(normalize_cpp_whitespace)
10518                .unwrap_or_default();
10519            self.record_type_aliases(
10520                ancestry,
10521                node,
10522                scope,
10523                vec![recovered.name],
10524                signature,
10525                range,
10526                &cpp_callable_lexical_scope(node, self.source, ancestry),
10527            );
10528            return;
10529        }
10530
10531        let alias_names = match node.kind() {
10532            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10533                .into_iter()
10534                .collect::<Vec<_>>(),
10535            "type_definition" => extract_typedef_alias_names(node, self.source),
10536            _ => Vec::new(),
10537        };
10538        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10539            (type_node, alias_names.as_slice())
10540            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10541            && type_node.child_by_field_name("name").is_none()
10542        {
10543            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10544        } else {
10545            None
10546        };
10547        self.add_type_aliases(ancestry, node, scope, alias_names);
10548        if let Some((body, alias_name)) = anonymous_aggregate {
10549            // The typedef alias is also the only user-visible identity of an
10550            // anonymous aggregate. Reuse it as the member owner instead of
10551            // minting a second signatureless class with the same FQN. The
10552            // latter makes forward lookup ambiguous when conditional aliases
10553            // coexist and returns duplicate definitions even without guards.
10554            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10555            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10556            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10557            let mut nested_scope = scope.clone();
10558            nested_scope.class_unit = Some(alias_unit);
10559            nested_scope.template_signature = scope.template_signature.clone();
10560            nested_scope.template_metadata = None;
10561            nested_scope.declarations_are_fields = false;
10562            nested_scope.recovered_specialization_member_scope = false;
10563            stack.push(CppWork::Container(CppContainer {
10564                node: body,
10565                scope: nested_scope,
10566            }));
10567        }
10568    }
10569
10570    fn add_type_aliases(
10571        &mut self,
10572        ancestry: &ParentIndex<'_>,
10573        node: Node<'_>,
10574        scope: &ScopeInfo,
10575        alias_names: Vec<String>,
10576    ) {
10577        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10578        self.record_type_aliases(
10579            ancestry,
10580            node,
10581            scope,
10582            alias_names,
10583            signature,
10584            cpp_declaration_range(node),
10585            &cpp_callable_lexical_scope(node, self.source, ancestry),
10586        );
10587    }
10588
10589    /// `lexical_scope` is where the alias target's names are looked up: the
10590    /// alias's CST ancestry, or the owner a structural recovery proved when
10591    /// the CST ancestry is malformed.
10592    #[allow(clippy::too_many_arguments)]
10593    fn record_type_aliases(
10594        &mut self,
10595        ancestry: &ParentIndex<'_>,
10596        node: Node<'_>,
10597        scope: &ScopeInfo,
10598        alias_names: Vec<String>,
10599        signature: String,
10600        range: Range,
10601        lexical_scope: &[String],
10602    ) {
10603        if signature.is_empty() {
10604            return;
10605        }
10606        let type_name = node
10607            .child_by_field_name("type")
10608            .and_then(|type_node| type_node.child_by_field_name("name"))
10609            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10610        for alias_name in alias_names {
10611            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10612                continue;
10613            }
10614            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10615            // Declaration identity does not include the alias signature. Keep
10616            // each physical range so conditional aliases retain their guards.
10617            self.note_declaration(&code_unit);
10618            self.link_source_declaration(ancestry, &code_unit, node);
10619            self.parsed
10620                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10621            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10622                cpp_structured_type_identity(type_node, self.source, lexical_scope)
10623            });
10624            self.add_signature_with_metadata(
10625                code_unit.clone(),
10626                SignatureMetadata::new(signature.clone(), Vec::new())
10627                    .with_underlying_type_identity(underlying_type_identity),
10628            );
10629            if let Some(metadata) = &scope.template_metadata {
10630                let mut metadata = metadata.clone();
10631                metadata.primary_fq_name = code_unit.fq_name();
10632                self.parsed
10633                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10634            }
10635            if let Some(parent) = &scope.class_unit {
10636                self.parsed.add_child(parent.clone(), code_unit.clone());
10637            } else if let Some(module) = &scope.module {
10638                self.parsed.add_child(module.clone(), code_unit.clone());
10639            }
10640            self.parsed.mark_type_alias(code_unit);
10641        }
10642    }
10643
10644    fn type_alias_unit(
10645        &self,
10646        scope: &ScopeInfo,
10647        alias_name: String,
10648        signature: String,
10649    ) -> CodeUnit {
10650        let short_name = if let Some(parent) = &scope.class_unit {
10651            cpp_join_nested_short(parent.short_name(), &alias_name)
10652        } else {
10653            alias_name.clone()
10654        };
10655        let fq = cpp_leaf_fq(
10656            &scope.package_name,
10657            scope.class_unit.as_ref(),
10658            &alias_name,
10659            SegmentKind::Nested,
10660            SegmentKind::Type,
10661        );
10662        CodeUnit::with_signature_and_fq(
10663            self.file.clone(),
10664            CodeUnitType::Class,
10665            scope.package_name.clone(),
10666            short_name,
10667            Some(signature),
10668            false,
10669            fq,
10670        )
10671    }
10672
10673    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10674        // A comment can terminate tree-sitter's preproc_arg before the logical
10675        // directive ends. Its remaining declarations are replacement locals,
10676        // not file-scope fields, even when recovery exposes them as siblings.
10677        if let Some(replacement) =
10678            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10679        {
10680            self.consumed_fragment_regions
10681                .push((replacement.start, replacement.end));
10682        }
10683        let Some(name) = extract_macro_name(node, self.source) else {
10684            return;
10685        };
10686        let signature = node_text(node, self.source).trim_end().to_string();
10687        if signature.is_empty() {
10688            return;
10689        }
10690        let fq = cpp_member_fq("", &name);
10691        // A macro can be undefined and redefined later in the same file. Its
10692        // structured directive is part of the declaration identity so the
10693        // temporal environment can navigate to the definition active at a
10694        // reference instead of collapsing every spelling to the first range.
10695        // The same physical directive parsed through another C/C++ reading
10696        // still produces the same unit and remains deduplicated.
10697        let code_unit = CodeUnit::with_signature_and_fq(
10698            self.file.clone(),
10699            CodeUnitType::Macro,
10700            "",
10701            name.clone(),
10702            Some(signature.clone()),
10703            false,
10704            fq,
10705        );
10706        if !self.parsed.contains_declaration(&code_unit) {
10707            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10708            let name_range = node
10709                .child_by_field_name("name")
10710                .map(cpp_declaration_range)
10711                .unwrap_or_else(|| cpp_declaration_range(node));
10712            self.parsed
10713                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10714                    site: cpp_declaration_range(node),
10715                    argument: name_range,
10716                    kind: GenerationKind::PreprocessorDefinition,
10717                    unit: code_unit.clone(),
10718                });
10719            self.parsed.add_signature(code_unit, signature);
10720        }
10721        if node.kind() == "preproc_def" {
10722            update_object_macro_field_environment(
10723                node,
10724                self.source,
10725                &mut self.object_macro_fields,
10726                &mut self.ambiguous_object_macro_fields,
10727            );
10728        } else {
10729            self.object_macro_fields.remove(&name);
10730            self.ambiguous_object_macro_fields.remove(&name);
10731        }
10732    }
10733
10734    /// Bare object-like field-list macros inside an otherwise well-formed
10735    /// aggregate remain identifier nodes in a field declaration. More than
10736    /// one adjacent invocation can be folded into the same declaration, so
10737    /// inspect all of its structured identifier nodes.
10738    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10739        if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10740            return false;
10741        }
10742        // Claiming the declaration means its members are accounted for, which
10743        // is only true when there is an owner to materialize them into. A file
10744        // scope reaches this with a declaration that merely *contains* an
10745        // aggregate spelling an invocation (tree-sitter wraps libuv's
10746        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10747        // `field_declaration` under a malformed header), and consuming it there
10748        // would discard the aggregate itself (#2985).
10749        if scope.class_unit.is_none() {
10750            return false;
10751        }
10752        let macro_nodes =
10753            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10754        for macro_node in &macro_nodes {
10755            let name = node_text(*macro_node, self.source).trim();
10756            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10757            self.materialize_object_macro_fields(
10758                fields,
10759                cpp_declaration_range(*macro_node),
10760                *macro_node,
10761                scope,
10762            );
10763        }
10764        let Some(last) = macro_nodes.last() else {
10765            return false;
10766        };
10767        // Claiming the declaration accounts for every member it spells, not
10768        // only the donated ones: an invocation folded together with the
10769        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10770        // leaves those members shapeless, and the grammar recovers them from
10771        // the byte range that follows the invocation.
10772        self.record_collapsed_aggregate_fields(
10773            last.end_byte()..node.end_byte(),
10774            node.start_position().row
10775                + 1
10776                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10777            node,
10778            scope,
10779        );
10780        true
10781    }
10782
10783    /// Retain the grammar's field interpretation with the original invocation
10784    /// or aggregate context. The replacement AST owns the type, while only the
10785    /// original source owns activation, guards, and occurrence ranges.
10786    fn capture_recovered_field_facts(
10787        &mut self,
10788        unit: &CodeUnit,
10789        range: Range,
10790        context: Node<'_>,
10791        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10792        names_function_type: bool,
10793    ) {
10794        let declaration = *self.source_links[unit]
10795            .last()
10796            .expect("recovered field follows its source occurrence");
10797        let mut fact = crate::source_facts::capture_declaration(
10798            declaration,
10799            context,
10800            unit,
10801            self.source,
10802            self.primary_ancestry,
10803        );
10804        fact.field_type = field_type;
10805        fact.names_function_type = names_function_type;
10806        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10807        let captured = crate::source_context::capture_declaration_context(
10808            context,
10809            unit,
10810            self.source,
10811            &mut self.preprocessor_context_cache,
10812        );
10813        fact.guard_requirements = captured.guard_requirements;
10814        fact.callable_guards = fact.guard_requirements.clone();
10815        fact.callable_activation = Some(range.end_byte);
10816        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10817        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10818        let intern = |node: Node<'_>| {
10819            let mut collector = self.source_collector.borrow_mut();
10820            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10821                collector.intern_node(node)
10822            } else {
10823                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10824            }
10825        };
10826        fact.conditional_family =
10827            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10828        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10829        fact.displaced_namespace_closing_brace =
10830            captured.displaced_namespace_closing_brace.map(intern);
10831        self.capture_source_dependencies(&mut fact, context, unit);
10832        self.apply_active_source_context(&mut fact);
10833        assert!(
10834            self.source_properties
10835                .borrow_mut()
10836                .insert(declaration, fact)
10837                .is_none()
10838        );
10839    }
10840
10841    fn materialize_object_macro_fields(
10842        &mut self,
10843        fields: Vec<MacroReplacementField>,
10844        range: Range,
10845        context: Node<'_>,
10846        scope: &ScopeInfo,
10847    ) {
10848        let Some(owner) = scope.class_unit.as_ref() else {
10849            return;
10850        };
10851        for field in fields {
10852            let signature = field.declaration.clone();
10853            let mut fq = owner.fq().clone();
10854            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10855            let short_name = if owner.short_name().is_empty() {
10856                field.name.clone()
10857            } else {
10858                format!("{}.{}", owner.short_name(), field.name)
10859            };
10860            let code_unit = CodeUnit::with_signature_and_fq(
10861                self.file.clone(),
10862                CodeUnitType::Field,
10863                owner.package_name().to_string(),
10864                short_name,
10865                Some(field.declaration),
10866                true,
10867                fq,
10868            );
10869            if self.parsed.contains_declaration(&code_unit) {
10870                continue;
10871            }
10872            self.add_declaration_with_range(
10873                self.primary_ancestry,
10874                code_unit.clone(),
10875                range,
10876                None,
10877                Some(owner.clone()),
10878                None,
10879            );
10880            self.capture_recovered_field_facts(
10881                &code_unit,
10882                range,
10883                context,
10884                field.field_type,
10885                field.names_function_type,
10886            );
10887            self.parsed.add_signature(code_unit, signature);
10888        }
10889    }
10890
10891    fn collapsed_aggregate_owner(
10892        &self,
10893        name: &str,
10894        scope: &ScopeInfo,
10895        parent: Option<&CodeUnit>,
10896    ) -> CodeUnit {
10897        let short_name = parent.map_or_else(
10898            || name.to_string(),
10899            |parent| cpp_join_nested_short(parent.short_name(), name),
10900        );
10901        let fq = cpp_leaf_fq(
10902            &scope.package_name,
10903            parent,
10904            name,
10905            SegmentKind::Nested,
10906            SegmentKind::Type,
10907        );
10908        CodeUnit::with_signature_and_fq(
10909            self.file.clone(),
10910            CodeUnitType::Class,
10911            scope.package_name.clone(),
10912            short_name,
10913            None,
10914            false,
10915            fq,
10916        )
10917    }
10918
10919    /// Recover ordinary aggregates whose field-list macro invocations made
10920    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
10921    /// The class markers, aggregate braces, field nodes, and macro identifier
10922    /// nodes are all read from the AST; no source delimiter or token scan is
10923    /// used. A close-brace node inside a malformed field closes the innermost
10924    /// recovered aggregate, preserving nested-owner identity.
10925    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
10926        let mut cursor = node.walk();
10927        let children = node.children(&mut cursor).collect::<Vec<_>>();
10928        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
10929        let mut object_macro_fields = self.object_macro_fields.clone();
10930        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
10931        let mut open = Vec::<usize>::new();
10932        let mut index = 0;
10933        while index < children.len() {
10934            let keyword = children[index];
10935            if update_object_macro_field_environment(
10936                keyword,
10937                self.source,
10938                &mut object_macro_fields,
10939                &mut ambiguous_object_macro_fields,
10940            ) {
10941                index += 1;
10942                continue;
10943            }
10944            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
10945                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
10946                if !name.is_empty() {
10947                    let parent = open
10948                        .last()
10949                        .and_then(|class| recovered.get(*class))
10950                        .map(|(owner, _, _, _)| owner.clone())
10951                        .or_else(|| scope.class_unit.clone());
10952                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
10953                    recovered.push((
10954                        owner,
10955                        head.key.start_byte(),
10956                        head.opening.end_byte(),
10957                        Vec::new(),
10958                    ));
10959                    let class_index = recovered.len() - 1;
10960                    match head.folded_members {
10961                        // The ordinary shape leaves the aggregate open: every
10962                        // child that follows belongs to it until a close brace
10963                        // ends it.
10964                        None => open.push(class_index),
10965                        // A folded head carries its own member list and its own
10966                        // closing brace, so it closes here. The invocations that
10967                        // precede the class key are the dangling tail of the
10968                        // replacement that folded it, and belong to whatever
10969                        // aggregate is open around it, not to this one.
10970                        Some(members) => {
10971                            let macro_nodes = object_macro_identifier_nodes_with_environment(
10972                                children[index],
10973                                self.source,
10974                                &mut object_macro_fields,
10975                                &mut ambiguous_object_macro_fields,
10976                            );
10977                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
10978                                .iter()
10979                                .partition(|node| node.start_byte() < head.key.start_byte());
10980                            if let Some(&enclosing) = open.last() {
10981                                for macro_node in preceding {
10982                                    recovered[enclosing]
10983                                        .3
10984                                        .push(CppCollapsedMember::MacroFields {
10985                                            context: macro_node,
10986                                            range: cpp_declaration_range(macro_node),
10987                                            fields: object_macro_field_closure(
10988                                                &object_macro_fields,
10989                                                &normalize_cpp_whitespace(node_text(
10990                                                    macro_node,
10991                                                    self.source,
10992                                                )),
10993                                            ),
10994                                        });
10995                                }
10996                            }
10997                            let closing = cpp_collapsed_aggregate_closing_brace(members);
10998                            recovered[class_index]
10999                                .3
11000                                .extend(cpp_collapsed_aggregate_members(
11001                                    members,
11002                                    &inner,
11003                                    head.opening.end_byte()..closing,
11004                                    head.opening.end_position().row + 1,
11005                                    self.source,
11006                                    &object_macro_fields,
11007                                ));
11008                            recovered[class_index].2 = members.end_byte();
11009                            for &open_class in &open {
11010                                recovered[open_class].2 =
11011                                    recovered[open_class].2.max(members.end_byte());
11012                            }
11013                        }
11014                    }
11015                    index += head.width;
11016                    continue;
11017                }
11018            }
11019            if let Some(&class_index) = open.last()
11020                && children[index].kind() == "field_declaration"
11021            {
11022                let field = children[index];
11023                let macro_nodes = object_macro_identifier_nodes_with_environment(
11024                    field,
11025                    self.source,
11026                    &mut object_macro_fields,
11027                    &mut ambiguous_object_macro_fields,
11028                );
11029                recovered[class_index]
11030                    .3
11031                    .extend(cpp_collapsed_aggregate_members(
11032                        field,
11033                        &macro_nodes,
11034                        field.start_byte()..field.end_byte(),
11035                        field.start_position().row + 1,
11036                        self.source,
11037                        &object_macro_fields,
11038                    ));
11039                let end = field.end_byte();
11040                for &open_class in &open {
11041                    recovered[open_class].2 = recovered[open_class].2.max(end);
11042                }
11043                let closes = count_close_brace_nodes(field);
11044                for _ in 0..closes {
11045                    if let Some(closed) = open.pop() {
11046                        recovered[closed].2 = end;
11047                    }
11048                }
11049            }
11050            index += 1;
11051        }
11052
11053        let mut owners = Vec::with_capacity(recovered.len());
11054        for (owner, start, end, members) in recovered {
11055            let parent = owners
11056                .iter()
11057                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11058                .cloned()
11059                .or_else(|| scope.class_unit.clone());
11060            self.declare_collapsed_aggregate(
11061                owner.clone(),
11062                start..end,
11063                Some(node),
11064                parent,
11065                members,
11066                scope,
11067            );
11068            owners.push(owner);
11069        }
11070    }
11071
11072    /// Record one recovered aggregate and the members it declares.
11073    fn declare_collapsed_aggregate(
11074        &mut self,
11075        owner: CodeUnit,
11076        span: std::ops::Range<usize>,
11077        evidence: Option<Node<'_>>,
11078        parent: Option<CodeUnit>,
11079        members: Vec<CppCollapsedMember<'_>>,
11080        scope: &ScopeInfo,
11081    ) {
11082        let range = Range {
11083            start_byte: span.start,
11084            end_byte: span.end,
11085            start_line: self.source.get(..span.start).map_or(1, |source| {
11086                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11087            }),
11088            end_line: self.source.get(..span.end).map_or(1, |source| {
11089                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11090            }),
11091        };
11092        // The tag can already be declared by a typedef or a forward
11093        // declaration, and this range is the definition's. Record it either
11094        // way: a declaration carries every range it is written at, and
11095        // dropping this one leaves nothing able to prove that the aggregate
11096        // has a body (#3098). Recording a range that is already held, or a
11097        // unit that is already declared, changes nothing.
11098        self.add_declaration_with_range(
11099            self.primary_ancestry,
11100            owner.clone(),
11101            range,
11102            evidence,
11103            parent,
11104            None,
11105        );
11106        // The recovery admitted this owner from an aggregate head and body.
11107        // Its malformed outer node need not itself be a struct_specifier, so
11108        // retain that structured definition proof explicitly.
11109        let declaration = *self.source_links[&owner]
11110            .last()
11111            .expect("collapsed aggregate has a source occurrence");
11112        {
11113            let mut properties = self.source_properties.borrow_mut();
11114            let fact = properties
11115                .get_mut(&declaration)
11116                .expect("collapsed aggregate retains its original context");
11117            fact.class_strength =
11118                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11119            fact.occurrence_role =
11120                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11121        }
11122        let owner_scope = ScopeInfo {
11123            class_unit: Some(owner),
11124            declarations_are_fields: true,
11125            ..scope.clone()
11126        };
11127        for member in members {
11128            match member {
11129                CppCollapsedMember::MacroFields {
11130                    range,
11131                    fields,
11132                    context,
11133                } => {
11134                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11135                }
11136                CppCollapsedMember::Declarations {
11137                    span,
11138                    start_line,
11139                    context,
11140                } => {
11141                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11142                }
11143            }
11144        }
11145    }
11146
11147    /// Recover an aggregate whose head tree-sitter folded into the declaration
11148    /// that precedes it, outside any collapsed `ERROR` container.
11149    ///
11150    /// A comment inside a field-list macro's replacement ends the replacement
11151    /// token, so the rest of the replacement is read at file scope. When the
11152    /// aggregate that follows declares its member list with one invocation,
11153    /// the parser reads the dangling tail as that declaration's type, the
11154    /// class key as an `ERROR`, and the member list as an initializer (#3098).
11155    /// The invocations before the class key belong to the replacement, not to
11156    /// this aggregate, so they go to whatever aggregate encloses it.
11157    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11158        if node.parent().is_some_and(|parent| parent.is_error()) {
11159            // The container's own recovery reads this child in source order,
11160            // with the preprocessor environment its earlier children build.
11161            return false;
11162        }
11163        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11164            return false;
11165        };
11166        let members = head
11167            .folded_members
11168            .expect("a folded aggregate head carries its member list");
11169        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11170        if name.is_empty() {
11171            return false;
11172        }
11173        let macro_nodes =
11174            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11175        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11176            .iter()
11177            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11178        for macro_node in preceding {
11179            let fields = object_macro_field_closure(
11180                &self.object_macro_fields,
11181                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11182            );
11183            self.materialize_object_macro_fields(
11184                fields,
11185                cpp_declaration_range(macro_node),
11186                macro_node,
11187                scope,
11188            );
11189        }
11190        let parent = scope.class_unit.clone();
11191        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11192        let recovered = cpp_collapsed_aggregate_members(
11193            members,
11194            &inner,
11195            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11196            head.opening.end_position().row + 1,
11197            self.source,
11198            &self.object_macro_fields,
11199        );
11200        self.declare_collapsed_aggregate(
11201            owner,
11202            head.key.start_byte()..members.end_byte(),
11203            Some(node),
11204            parent,
11205            recovered,
11206            scope,
11207        );
11208        true
11209    }
11210
11211    /// Record the members of a collapsed aggregate region as fields of its
11212    /// recovered owner. The names, declaration text, and byte ranges all come
11213    /// from the grammar's reading of the region, so a region that is not a
11214    /// member list contributes nothing.
11215    fn record_collapsed_aggregate_fields(
11216        &mut self,
11217        span: std::ops::Range<usize>,
11218        start_line: usize,
11219        context: Node<'_>,
11220        scope: &ScopeInfo,
11221    ) {
11222        let Some(owner) = scope.class_unit.as_ref() else {
11223            return;
11224        };
11225        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11226            let range = Range {
11227                start_byte: field.range.start,
11228                end_byte: field.range.end,
11229                start_line: start_line
11230                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
11231                end_line: start_line
11232                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
11233            };
11234            let mut fq = owner.fq().clone();
11235            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11236            let short_name = if owner.short_name().is_empty() {
11237                field.name.clone()
11238            } else {
11239                format!("{}.{}", owner.short_name(), field.name)
11240            };
11241            let signature = normalize_cpp_whitespace(&field.declaration);
11242            let code_unit = CodeUnit::with_signature_and_fq(
11243                self.file.clone(),
11244                CodeUnitType::Field,
11245                owner.package_name().to_string(),
11246                short_name,
11247                Some(signature.clone()),
11248                false,
11249                fq,
11250            );
11251            if self.parsed.contains_declaration(&code_unit) {
11252                continue;
11253            }
11254            self.add_declaration_with_range(
11255                self.primary_ancestry,
11256                code_unit.clone(),
11257                range,
11258                None,
11259                Some(owner.clone()),
11260                None,
11261            );
11262            self.capture_recovered_field_facts(
11263                &code_unit,
11264                range,
11265                context,
11266                field.field_type,
11267                field.names_function_type,
11268            );
11269            self.parsed.add_signature(code_unit, signature);
11270        }
11271    }
11272
11273    fn visit_preproc_call(&mut self, node: Node<'_>) {
11274        let Some(_directive) = node.child_by_field_name("directive") else {
11275            return;
11276        };
11277        if is_cpp_undef_directive(node, self.source) {
11278            update_object_macro_field_environment(
11279                node,
11280                self.source,
11281                &mut self.object_macro_fields,
11282                &mut self.ambiguous_object_macro_fields,
11283            );
11284        }
11285    }
11286}
11287
11288/// Every member a field-list macro contributes under `environment`, including
11289/// the members of the field-list macros it composes.
11290///
11291/// Composition is resolved at the invocation, not at the definition, because a
11292/// nested name's active replacement is a property of the environment the
11293/// invocation sees. The traversal is iterative and visits each name once, so a
11294/// self-composing or mutually composing pair terminates.
11295fn object_macro_field_closure(
11296    environment: &HashMap<String, ObjectMacroReplacement>,
11297    name: &str,
11298) -> Vec<MacroReplacementField> {
11299    let mut fields = Vec::new();
11300    let mut visited = HashSet::default();
11301    let mut stack = vec![name.to_string()];
11302    while let Some(current) = stack.pop() {
11303        if !visited.insert(current.clone()) {
11304            continue;
11305        }
11306        let Some(replacement) = environment.get(&current) else {
11307            continue;
11308        };
11309        fields.extend(replacement.fields.iter().cloned());
11310        stack.extend(replacement.nested.iter().rev().cloned());
11311    }
11312    fields
11313}
11314
11315/// The structured replacement an object-like `#define` contributes.
11316///
11317/// The directive's whole logical line is the replacement list, which
11318/// [`object_macro_replacement_span`] recovers, because a comment inside the
11319/// replacement ends tree-sitter's `preproc_arg` token early.
11320fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11321    crate::graph::syntax::object_macro_replacement_span(node, source)
11322        .and_then(|span| source.get(span))
11323        .map(crate::graph::syntax::object_macro_replacement)
11324        .unwrap_or_default()
11325}
11326
11327/// Apply one preprocessor directive to an object-like field-list environment.
11328/// The environment is intentionally separate from the declaration visitor so
11329/// malformed parser regions can replay directives in source order without
11330/// mutating the live walk's state ahead of those directives.
11331fn update_object_macro_field_environment(
11332    node: Node<'_>,
11333    source: &str,
11334    fields: &mut HashMap<String, ObjectMacroReplacement>,
11335    ambiguous: &mut HashSet<String>,
11336) -> bool {
11337    match node.kind() {
11338        "preproc_def" => {
11339            let Some(name) = extract_macro_name(node, source) else {
11340                return false;
11341            };
11342            let replacement = object_macro_replacement_of(node, source);
11343            if replacement.is_empty() || ambiguous.contains(&name) {
11344                fields.remove(&name);
11345                ambiguous.insert(name);
11346            } else if let Some(previous) = fields.get(&name) {
11347                if previous != &replacement {
11348                    fields.remove(&name);
11349                    ambiguous.insert(name);
11350                }
11351            } else {
11352                fields.insert(name, replacement);
11353            }
11354            true
11355        }
11356        "preproc_call" if is_cpp_undef_directive(node, source) => {
11357            if let Some(argument) = node.child_by_field_name("argument") {
11358                let name = node_text(argument, source).trim();
11359                fields.remove(name);
11360                if inside_preprocessor_conditional(node) {
11361                    ambiguous.insert(name.to_string());
11362                } else {
11363                    ambiguous.remove(name);
11364                }
11365            }
11366            true
11367        }
11368        _ => false,
11369    }
11370}
11371
11372fn object_macro_identifier_nodes<'tree>(
11373    node: Node<'tree>,
11374    source: &str,
11375    fields: &HashMap<String, ObjectMacroReplacement>,
11376) -> Vec<Node<'tree>> {
11377    walk_object_macro_identifier_nodes(node, |current, result| {
11378        if matches!(
11379            current.kind(),
11380            "identifier" | "field_identifier" | "type_identifier"
11381        ) && fields.contains_key(node_text(current, source).trim())
11382        {
11383            result.push(current);
11384        }
11385        false
11386    })
11387}
11388
11389fn walk_object_macro_identifier_nodes<'tree>(
11390    node: Node<'tree>,
11391    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11392) -> Vec<Node<'tree>> {
11393    let mut result = Vec::new();
11394    let mut stack = vec![node];
11395    while let Some(current) = stack.pop() {
11396        if visit(current, &mut result) {
11397            continue;
11398        }
11399        let mut cursor = current.walk();
11400        let children = current.children(&mut cursor).collect::<Vec<_>>();
11401        stack.extend(children.into_iter().rev());
11402    }
11403    result.sort_by_key(|node| node.start_byte());
11404    result
11405}
11406
11407/// Collect macro identifiers while replaying any nested preprocessor
11408/// directives in source order. Recovery regions can contain a later `#undef`
11409/// in the same parser error envelope; using the live visitor map for the
11410/// entire envelope would incorrectly materialize invocations after it.
11411fn object_macro_identifier_nodes_with_environment<'tree>(
11412    node: Node<'tree>,
11413    source: &str,
11414    fields: &mut HashMap<String, ObjectMacroReplacement>,
11415    ambiguous: &mut HashSet<String>,
11416) -> Vec<Node<'tree>> {
11417    walk_object_macro_identifier_nodes(node, |current, result| {
11418        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11419            return true;
11420        }
11421        if matches!(
11422            current.kind(),
11423            "identifier" | "field_identifier" | "type_identifier"
11424        ) && fields.contains_key(node_text(current, source).trim())
11425        {
11426            result.push(current);
11427        }
11428        false
11429    })
11430}
11431
11432/// One member region of an aggregate whose head and body tree-sitter collapsed
11433/// into an `ERROR` container, in source order.
11434enum CppCollapsedMember<'tree> {
11435    /// The members a field-list macro invocation donates, all sharing the
11436    /// invocation's range.
11437    MacroFields {
11438        context: Node<'tree>,
11439        range: Range,
11440        fields: Vec<MacroReplacementField>,
11441    },
11442    /// A byte span whose ordinary member declarations lost their shape with
11443    /// the aggregate body, with the 1-based line the span starts on.
11444    Declarations {
11445        context: Node<'tree>,
11446        span: std::ops::Range<usize>,
11447        start_line: usize,
11448    },
11449}
11450
11451/// One aggregate head recovered from the children of a collapsed `ERROR`
11452/// container.
11453///
11454/// A member list that opens with an object-like field-list macro invocation
11455/// has no grammar rule, so the aggregate never forms a specifier and its head
11456/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11457/// class key, the name, and the opening brace are three consecutive children.
11458/// When the aggregate follows the dangling tail of a comment-truncated
11459/// replacement, the parser reads that tail as the declaration's type and folds
11460/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11461/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11462/// }` that follows becomes one `field_declaration` whose class key is an
11463/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11464/// `initializer_list` (#3098).
11465struct CppCollapsedAggregateHead<'tree> {
11466    key: Node<'tree>,
11467    name: Node<'tree>,
11468    opening: Node<'tree>,
11469    /// The member list when the head folded it into its own node, which also
11470    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11471    /// for the children that follow it.
11472    folded_members: Option<Node<'tree>>,
11473    /// How many children the head consumes.
11474    width: usize,
11475}
11476
11477/// The aggregate head that starts at `children[index]`, if one does.
11478fn cpp_collapsed_aggregate_head<'tree>(
11479    children: &[Node<'tree>],
11480    index: usize,
11481    source: &str,
11482) -> Option<CppCollapsedAggregateHead<'tree>> {
11483    let key = *children.get(index)?;
11484    if matches!(key.kind(), "struct" | "class" | "union") {
11485        let name = *children.get(index + 1)?;
11486        let opening = *children.get(index + 2)?;
11487        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11488            return None;
11489        }
11490        return Some(CppCollapsedAggregateHead {
11491            key,
11492            name,
11493            opening,
11494            folded_members: None,
11495            width: 3,
11496        });
11497    }
11498    cpp_folded_aggregate_head(key, source)
11499}
11500
11501/// The aggregate head tree-sitter folded into one declaration node, reading
11502/// the class key as an `ERROR` and the member list as an initializer.
11503fn cpp_folded_aggregate_head<'tree>(
11504    node: Node<'tree>,
11505    source: &str,
11506) -> Option<CppCollapsedAggregateHead<'tree>> {
11507    if !matches!(node.kind(), "declaration" | "field_declaration") {
11508        return None;
11509    }
11510    let mut cursor = node.walk();
11511    let children = node.children(&mut cursor).collect::<Vec<_>>();
11512    let key_index = children.iter().position(|child| {
11513        child.is_error()
11514            && matches!(
11515                node_text(*child, source).trim(),
11516                "struct" | "class" | "union"
11517            )
11518    })?;
11519    let declarator = *children.get(key_index + 1)?;
11520    // At file scope the grammar has an `init_declarator` for the name and its
11521    // initializer; inside an `ERROR` container the two are loose siblings.
11522    let (name, members) = if declarator.kind() == "init_declarator" {
11523        (
11524            declarator.child_by_field_name("declarator")?,
11525            declarator.child_by_field_name("value")?,
11526        )
11527    } else {
11528        (declarator, *children.get(key_index + 2)?)
11529    };
11530    if !matches!(
11531        name.kind(),
11532        "field_identifier" | "type_identifier" | "identifier"
11533    ) {
11534        return None;
11535    }
11536    if members.kind() != "initializer_list" {
11537        return None;
11538    }
11539    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11540    Some(CppCollapsedAggregateHead {
11541        key: children[key_index],
11542        name,
11543        opening,
11544        folded_members: Some(members),
11545        width: 1,
11546    })
11547}
11548
11549/// Where a folded member list ends: at its own closing brace when it has one,
11550/// so the reparse of the declarations it holds sees a member list and not a
11551/// stray brace.
11552fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11553    let mut cursor = members.walk();
11554    members
11555        .children(&mut cursor)
11556        .filter(|child| child.kind() == "}" && !child.is_missing())
11557        .last()
11558        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11559}
11560
11561/// The members one collapsed aggregate region declares, in source order: what
11562/// its field-list macro invocations donate, then the ordinary declarations
11563/// that follow the last invocation and lost their shape with the body.
11564fn cpp_collapsed_aggregate_members<'tree>(
11565    context: Node<'tree>,
11566    macro_nodes: &[Node<'tree>],
11567    region: std::ops::Range<usize>,
11568    region_start_line: usize,
11569    source: &str,
11570    environment: &HashMap<String, ObjectMacroReplacement>,
11571) -> Vec<CppCollapsedMember<'tree>> {
11572    let mut members = macro_nodes
11573        .iter()
11574        .map(|macro_node| CppCollapsedMember::MacroFields {
11575            context: *macro_node,
11576            range: cpp_declaration_range(*macro_node),
11577            fields: object_macro_field_closure(
11578                environment,
11579                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11580            ),
11581        })
11582        .collect::<Vec<_>>();
11583    // Whatever follows the last invocation is an ordinary member list that
11584    // lost its declaration shape with the aggregate's body; the grammar
11585    // recovers it from its own byte range.
11586    let declarations_start = macro_nodes
11587        .last()
11588        .map_or(region.start, |macro_node| macro_node.end_byte());
11589    members.push(CppCollapsedMember::Declarations {
11590        context,
11591        span: declarations_start..region.end,
11592        start_line: region_start_line
11593            + cpp_line_breaks_between(source, region.start, declarations_start),
11594    });
11595    members
11596}
11597
11598/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11599/// line without rescanning the file from its start.
11600fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11601    source.get(from..to).map_or(0, |slice| {
11602        slice.bytes().filter(|byte| *byte == b'\n').count()
11603    })
11604}
11605
11606/// How many enclosing aggregates a collapsed member declaration closes: the
11607/// close braces it carries that its own braces do not balance. A member that
11608/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11609/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11610/// owner early and lost every member after it (#2985).
11611fn count_close_brace_nodes(node: Node<'_>) -> usize {
11612    let mut opened = 0usize;
11613    let mut closed = 0usize;
11614    let mut stack = vec![node];
11615    while let Some(current) = stack.pop() {
11616        if !current.is_missing() {
11617            match current.kind() {
11618                "{" => opened += 1,
11619                "}" => closed += 1,
11620                _ => {}
11621            }
11622        }
11623        let mut cursor = current.walk();
11624        stack.extend(current.children(&mut cursor));
11625    }
11626    closed.saturating_sub(opened)
11627}
11628
11629/// Classify a C++ field while its declaration syntax is already available.
11630///
11631/// The persisted result lets later visibility queries avoid reparsing the
11632/// complete source file only to recover linkage.
11633pub fn cpp_field_declaration_linkage<'tree>(
11634    declaration: Node<'tree>,
11635    source: &str,
11636    ancestry: &ParentIndex<'tree>,
11637) -> CppFieldLinkage {
11638    let mut current = ancestry.parent(declaration);
11639    let mut enclosed_by_class = false;
11640    while let Some(node) = current {
11641        if node.kind() == "namespace_definition"
11642            && node
11643                .child_by_field_name("name")
11644                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11645        {
11646            return CppFieldLinkage::Internal;
11647        }
11648        if matches!(
11649            node.kind(),
11650            "class_specifier" | "struct_specifier" | "union_specifier"
11651        ) && node
11652            .child_by_field_name("name")
11653            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11654        {
11655            return CppFieldLinkage::Internal;
11656        }
11657        if matches!(
11658            node.kind(),
11659            "class_specifier" | "struct_specifier" | "union_specifier"
11660        ) {
11661            enclosed_by_class = true;
11662        }
11663        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11664            return CppFieldLinkage::Internal;
11665        }
11666        current = ancestry.parent(node);
11667    }
11668    if enclosed_by_class {
11669        return CppFieldLinkage::External;
11670    }
11671    let mut cursor = declaration.walk();
11672    let mut has_static = false;
11673    let mut has_extern = false;
11674    let mut has_inline = false;
11675    let mut has_const = false;
11676    let mut has_constexpr = false;
11677    for child in declaration.named_children(&mut cursor) {
11678        let text = normalize_cpp_whitespace(node_text(child, source));
11679        match (child.kind(), text.as_str()) {
11680            ("storage_class_specifier", "static") => has_static = true,
11681            ("storage_class_specifier", "extern") => has_extern = true,
11682            ("storage_class_specifier", "inline") => has_inline = true,
11683            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11684            ("type_qualifier", "const") => has_const = true,
11685            ("type_qualifier", "constexpr") => has_constexpr = true,
11686            _ => {}
11687        }
11688    }
11689    if has_static {
11690        CppFieldLinkage::Internal
11691    } else if has_extern || has_inline {
11692        CppFieldLinkage::External
11693    } else if has_const || has_constexpr {
11694        CppFieldLinkage::InternalUnlessExternalPeer
11695    } else {
11696        CppFieldLinkage::External
11697    }
11698}
11699
11700fn cpp_declaration_range(node: Node<'_>) -> Range {
11701    Range {
11702        start_byte: node.start_byte(),
11703        end_byte: node.end_byte(),
11704        start_line: node.start_position().row + 1,
11705        end_line: node.end_position().row + 1,
11706    }
11707}
11708
11709/// A recovery interval as a [`Range`], for materialization records whose
11710/// window is a byte region rather than one parser node (the sentinel-macro
11711/// region reparses, issue #941/#1657).
11712fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11713    let line_at = |byte: usize| {
11714        source.as_bytes()[..byte]
11715            .iter()
11716            .filter(|&&b| b == b'\n')
11717            .count()
11718            + 1
11719    };
11720    Range {
11721        start_byte,
11722        end_byte,
11723        start_line: line_at(start_byte),
11724        end_line: line_at(end_byte),
11725    }
11726}
11727
11728fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11729    ImportInfo {
11730        raw_snippet,
11731        is_wildcard: false,
11732        is_global: false,
11733        identifier: None,
11734        alias: None,
11735        path: None,
11736        binder_span: None,
11737    }
11738}
11739
11740fn recover_quoted_source_imports(
11741    source: &str,
11742    imports: &mut Vec<SourceImportFact>,
11743    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11744    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11745) {
11746    let mut in_block_comment = false;
11747    let mut start = 0;
11748    for line in source.split_inclusive('\n') {
11749        let end = start + line.len();
11750        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11751        let trimmed = stripped.trim();
11752        if looks_like_quoted_include_line(trimmed) {
11753            let raw = normalize_cpp_whitespace(trimmed);
11754            if !imports.iter().any(|import| import.statement == raw) {
11755                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11756                    start,
11757                    end,
11758                    SourceOccurrenceProvenance::ExplicitSubspan,
11759                );
11760                // Recovery already identified a quoted directive hidden by a
11761                // malformed outer construct. Interpret its path with the same
11762                // grammar in an isolated included range, preserving byte identity.
11763                let region =
11764                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11765                let target = region.as_ref().and_then(|tree| {
11766                    let mut pending = vec![tree.root_node()];
11767                    while let Some(node) = pending.pop() {
11768                        if node.kind() == "preproc_include"
11769                            && let Some(path) = node.child_by_field_name("path")
11770                            && path.kind() == "string_literal"
11771                        {
11772                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11773                            let target = collector.borrow_mut().intern_embedded(
11774                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11775                            );
11776                            includes.push(
11777                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11778                                    declaration: occurrence,
11779                                    target,
11780                                    path: value.to_owned(),
11781                                    quoted: true,
11782                                },
11783                            );
11784                            return Some(target);
11785                        }
11786                        let mut cursor = node.walk();
11787                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11788                        pending.extend(children.into_iter().rev());
11789                    }
11790                    None
11791                });
11792                imports.push(SourceImportFact::from_import(
11793                    cpp_include_info(raw),
11794                    occurrence,
11795                    target,
11796                    None,
11797                    Vec::new(),
11798                ));
11799            }
11800        }
11801        start = end;
11802    }
11803}
11804
11805fn looks_like_quoted_include_line(line: &str) -> bool {
11806    let Some(rest) = line.trim_start().strip_prefix('#') else {
11807        return false;
11808    };
11809    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11810        return false;
11811    };
11812    rest.trim_start().starts_with('"')
11813}
11814
11815fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11816    let mut raw = Vec::new();
11817    if let Some(bases) = cpp_class_like_base_clause(node) {
11818        collect_cpp_base_nodes(bases, source, &mut raw);
11819    }
11820    raw
11821}
11822
11823fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11824    let mut facts = Vec::new();
11825    let mut cursor = node.walk();
11826    for child in node.named_children(&mut cursor) {
11827        if child.kind() != "base_class_clause" {
11828            continue;
11829        }
11830        let mut is_virtual = false;
11831        let mut child_cursor = child.walk();
11832        for base in child.children(&mut child_cursor) {
11833            match base.kind() {
11834                "virtual" => is_virtual = true,
11835                "," => is_virtual = false,
11836                "type_identifier"
11837                | "qualified_identifier"
11838                | "scoped_type_identifier"
11839                | "template_type"
11840                | "template_function" => {
11841                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11842                        base,
11843                        source,
11844                        is_virtual,
11845                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11846                    ) {
11847                        facts.push(fact);
11848                    }
11849                    is_virtual = false;
11850                }
11851                _ => {}
11852            }
11853        }
11854    }
11855    facts
11856}
11857
11858/// The `base_class_clause` a class-like node writes, if it writes one.
11859///
11860/// The pinned grammar exposes the clause as a named child rather than a field,
11861/// so the question is asked by kind. A base clause is the only place a C++
11862/// class body records that some other class may declare a member, which is
11863/// what [`cpp_callable_dispatch_extensibility`] reads it for.
11864fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
11865    let mut cursor = class_like.walk();
11866    class_like
11867        .named_children(&mut cursor)
11868        .find(|child| child.kind() == "base_class_clause")
11869}
11870
11871fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
11872    walk_named_tree_preorder(node, false, |child| match child.kind() {
11873        "type_identifier" | "qualified_identifier" | "template_type" => {
11874            let text = normalize_cpp_whitespace(node_text(child, source));
11875            if !text.is_empty() {
11876                raw.push(text);
11877            }
11878            WalkControl::SkipChildren
11879        }
11880        _ => WalkControl::Continue,
11881    });
11882}
11883
11884fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
11885    let mut out = String::new();
11886    let chars: Vec<char> = line.chars().collect();
11887    let mut index = 0;
11888    let mut in_string = false;
11889    let mut in_char = false;
11890    let mut escape = false;
11891
11892    while index < chars.len() {
11893        let ch = chars[index];
11894        let next = chars.get(index + 1).copied();
11895
11896        if *in_block_comment {
11897            if ch == '*' && next == Some('/') {
11898                *in_block_comment = false;
11899                index += 2;
11900            } else {
11901                index += 1;
11902            }
11903            continue;
11904        }
11905
11906        if in_string {
11907            out.push(ch);
11908            if escape {
11909                escape = false;
11910            } else if ch == '\\' {
11911                escape = true;
11912            } else if ch == '"' {
11913                in_string = false;
11914            }
11915            index += 1;
11916            continue;
11917        }
11918
11919        if in_char {
11920            out.push(ch);
11921            if escape {
11922                escape = false;
11923            } else if ch == '\\' {
11924                escape = true;
11925            } else if ch == '\'' {
11926                in_char = false;
11927            }
11928            index += 1;
11929            continue;
11930        }
11931
11932        if ch == '/' && next == Some('/') {
11933            break;
11934        }
11935        if ch == '/' && next == Some('*') {
11936            *in_block_comment = true;
11937            index += 2;
11938            continue;
11939        }
11940        if ch == '"' {
11941            in_string = true;
11942            out.push(ch);
11943            index += 1;
11944            continue;
11945        }
11946        if ch == '\'' {
11947            in_char = true;
11948            out.push(ch);
11949            index += 1;
11950            continue;
11951        }
11952
11953        out.push(ch);
11954        index += 1;
11955    }
11956
11957    out
11958}
11959
11960#[derive(Clone)]
11961struct FunctionInfo {
11962    package_name: String,
11963    owner: Option<CppMemberOwner>,
11964    name: String,
11965    signature: String,
11966}
11967
11968/// Owner of a member function, kept structured so a literal `$` inside a
11969/// source-level class name never crosses a join/split boundary: the legacy
11970/// `$`-joined owner string was re-split at fq construction, dropping a leading
11971/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
11972/// and tripping the package/short boundary assert -- the #2140 corruption one
11973/// level up (#2362).
11974#[derive(Clone)]
11975enum CppMemberOwner {
11976    /// Source-level owner class chain from a qualified declarator-id, one
11977    /// class name per component (`Outer::Inner::method` -> `["Outer",
11978    /// "Inner"]`); each component may itself contain a literal `$`.
11979    Chain(Vec<String>),
11980    /// The lexically enclosing or recovered class unit; the member fq extends
11981    /// its fq directly instead of re-splitting its `$`-joined short chain.
11982    Unit(CodeUnit),
11983}
11984
11985impl CppMemberOwner {
11986    /// The legacy `$`-joined owner chain used in the member's short name.
11987    fn short_chain(&self) -> String {
11988        match self {
11989            Self::Chain(chain) => chain.join("$"),
11990            Self::Unit(parent) => parent.short_name().to_string(),
11991        }
11992    }
11993}
11994
11995enum DeclaratorKind<'a> {
11996    Function(Node<'a>),
11997    Variable(Node<'a>),
11998}
11999
12000impl FunctionInfo {
12001    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
12002        self.code_unit_with_synthetic(file, false)
12003    }
12004
12005    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12006        let short_name = match &self.owner {
12007            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12008            None => self.name.clone(),
12009        };
12010        let fq = match &self.owner {
12011            Some(CppMemberOwner::Chain(chain)) => {
12012                debug_assert!(
12013                    !chain.is_empty(),
12014                    "an empty owner chain is no owner; producers return None instead"
12015                );
12016                let mut fq = FqName::new();
12017                cpp_push_package(&mut fq, &self.package_name);
12018                let mut first = true;
12019                for component in chain {
12020                    let kind = if first {
12021                        SegmentKind::Type
12022                    } else {
12023                        SegmentKind::Nested
12024                    };
12025                    fq.push(segment_interner().intern(component, kind));
12026                    first = false;
12027                }
12028                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12029                fq
12030            }
12031            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12032                .fq()
12033                .clone()
12034                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12035            // An anonymous parent (empty short chain) contributes no owner
12036            // segment -- the same guard as cpp_join_member_short above.
12037            Some(CppMemberOwner::Unit(_)) | None => {
12038                let mut fq = FqName::new();
12039                cpp_push_package(&mut fq, &self.package_name);
12040                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12041                fq
12042            }
12043        };
12044        CodeUnit::with_signature_and_fq(
12045            file,
12046            CodeUnitType::Function,
12047            self.package_name.clone(),
12048            short_name,
12049            Some(self.signature.clone()),
12050            synthetic,
12051            fq,
12052        )
12053    }
12054}
12055
12056fn extract_function_info(
12057    declarator: Node<'_>,
12058    source: &str,
12059    scope: &ScopeInfo,
12060) -> Option<FunctionInfo> {
12061    let parameters_node = declarator.child_by_field_name("parameters")?;
12062    let declarator_name_node = declarator
12063        .child_by_field_name("declarator")
12064        .or_else(|| parameters_node.prev_named_sibling())?;
12065    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12066}
12067
12068fn extract_function_info_from_name(
12069    declarator: Node<'_>,
12070    declarator_name_node: Node<'_>,
12071    source: &str,
12072    scope: &ScopeInfo,
12073) -> Option<FunctionInfo> {
12074    let parameters_node = declarator.child_by_field_name("parameters")?;
12075    let parameters_text = cpp_parameter_signature(parameters_node, source);
12076    let recovered_specialization_member = scope
12077        .recovered_specialization_member_scope
12078        .then(|| {
12079            let terminal = declarator_name_node
12080                .child_by_field_name("name")
12081                .unwrap_or(declarator_name_node);
12082            let name = canonical_cpp_qualified_component(terminal, source)?.name;
12083            let owner = scope.class_unit.as_ref()?;
12084            Some((
12085                Some(CppMemberOwner::Unit(owner.clone())),
12086                name,
12087                scope.package_name.clone(),
12088            ))
12089        })
12090        .flatten();
12091    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12092        parts
12093    } else if let Some(parts) =
12094        split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12095    {
12096        parts
12097    } else {
12098        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12099            declarator_name_node,
12100            source,
12101        )?);
12102        if raw_name.is_empty() {
12103            return None;
12104        }
12105        let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12106        match using_bound_cpp_owner(declarator_name_node, source, scope) {
12107            Some((owner_chain, package_name)) => {
12108                (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12109            }
12110            None => (owner, name, package_name),
12111        }
12112    };
12113    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12114    let mut signature = if suffix.is_empty() {
12115        parameters_text
12116    } else {
12117        format!("{parameters_text} {suffix}")
12118    };
12119    if let Some(template_signature) = &scope.template_signature {
12120        signature = format!("{template_signature}{signature}");
12121    }
12122
12123    Some(FunctionInfo {
12124        package_name,
12125        owner,
12126        name,
12127        signature,
12128    })
12129}
12130
12131/// Recover the semantic return type and callable name when a declaration macro
12132/// occupies a function definition's `type` field. Tree-sitter either exposes a
12133/// scalar return as the declarator's apparent name and the callable as the sole
12134/// identifier in an `ERROR`, or joins a template return and callable into a
12135/// qualified identifier with a missing `::`. Both shapes retain the complete
12136/// parameter list and body; a concrete separator remains an out-of-line member.
12137fn cpp_macro_displaced_callable_parts<'tree>(
12138    function_declarator: Node<'tree>,
12139    source: &str,
12140    ancestry: &ParentIndex<'tree>,
12141) -> Option<(Node<'tree>, Node<'tree>)> {
12142    let definition = ancestry.parent(function_declarator)?;
12143    if definition.kind() != "function_definition"
12144        || definition.child_by_field_name("declarator") != Some(function_declarator)
12145        || definition
12146            .child_by_field_name("body")
12147            .is_none_or(|body| body.kind() != "compound_statement")
12148    {
12149        return None;
12150    }
12151    let macro_type = definition.child_by_field_name("type")?;
12152    if macro_type.kind() != "type_identifier"
12153        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12154    {
12155        return None;
12156    }
12157
12158    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12159    if apparent_return_type.kind() == "qualified_identifier"
12160        && let (Some(return_type), Some(callable_name)) = (
12161            apparent_return_type.child_by_field_name("scope"),
12162            apparent_return_type.child_by_field_name("name"),
12163        )
12164        && return_type.kind() == "template_type"
12165        && matches!(callable_name.kind(), "identifier" | "field_identifier")
12166        && (0..apparent_return_type.child_count())
12167            .filter_map(|index| apparent_return_type.child(index))
12168            .any(|child| child.kind() == "::" && child.is_missing())
12169        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12170        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12171    {
12172        return Some((return_type, callable_name));
12173    }
12174    if !matches!(
12175        apparent_return_type.kind(),
12176        "identifier" | "field_identifier" | "type_identifier"
12177    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12178    {
12179        return None;
12180    }
12181    let parameters = function_declarator.child_by_field_name("parameters")?;
12182    let mut cursor = function_declarator.walk();
12183    let between = function_declarator
12184        .named_children(&mut cursor)
12185        .filter(|child| child.kind() != "comment")
12186        .filter(|child| {
12187            child.start_byte() >= apparent_return_type.end_byte()
12188                && child.end_byte() <= parameters.start_byte()
12189                && !same_node(*child, apparent_return_type)
12190                && !same_node(*child, parameters)
12191        })
12192        .collect::<Vec<_>>();
12193    let [name_error] = between.as_slice() else {
12194        return None;
12195    };
12196    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12197        return None;
12198    }
12199    let callable_name = name_error.named_child(0)?;
12200    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12201        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12202    {
12203        return None;
12204    }
12205    Some((apparent_return_type, callable_name))
12206}
12207
12208/// The part of a `function_declarator` after its parameter list that belongs to
12209/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
12210/// specification, a trailing return type and a trailing requires-clause.
12211///
12212/// The grammar makes each of these a distinct sibling of the `parameters`
12213/// field, so they are read from the tree. Splitting the declarator's text on
12214/// the parameter list instead silently dropped every qualifier whenever the
12215/// parameter list was spelled with whitespace that normalization rewrote - a
12216/// line break or a double space was enough to make a `const` member definition
12217/// a different logical symbol from its declaration (#1827).
12218///
12219/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
12220/// are deliberately excluded. C++ does not make them part of the signature and
12221/// an out-of-line definition never repeats them, so including them would split
12222/// a declaration from its own definition.
12223fn cpp_declarator_identity_suffix(
12224    declarator: Node<'_>,
12225    parameters_node: Node<'_>,
12226    source: &str,
12227) -> String {
12228    let mut cursor = declarator.walk();
12229    let parts = declarator
12230        .named_children(&mut cursor)
12231        .filter(|child| child.start_byte() >= parameters_node.end_byte())
12232        .filter(|child| {
12233            matches!(
12234                child.kind(),
12235                "type_qualifier"
12236                    | "ref_qualifier"
12237                    | "noexcept"
12238                    | "throw_specifier"
12239                    | "trailing_return_type"
12240                    | "requires_clause"
12241            )
12242        })
12243        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12244        .filter(|text| !text.is_empty())
12245        .collect::<Vec<_>>();
12246    normalize_cpp_qualifier_suffix(&parts.join(" "))
12247}
12248
12249/// The identity suffix of one callable declarator, for a consumer that holds
12250/// the declarator rather than the declaration walk's parts.
12251///
12252/// The persisted signature concatenates the parameter spelling and this suffix,
12253/// so a comparison that must agree on the suffix alone recomputes it here
12254/// instead of splitting the stored string.
12255pub(crate) fn cpp_callable_identity_suffix(
12256    function_declarator: Node<'_>,
12257    source: &str,
12258) -> Option<String> {
12259    let parameters_node = function_declarator.child_by_field_name("parameters")?;
12260    Some(cpp_declarator_identity_suffix(
12261        function_declarator,
12262        parameters_node,
12263        source,
12264    ))
12265}
12266
12267pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12268    match classify_declarator(node)? {
12269        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12270        DeclaratorKind::Variable(_) => None,
12271    }
12272}
12273
12274fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12275    match node.kind() {
12276        "function_declarator" => {
12277            let inner = node
12278                .child_by_field_name("declarator")
12279                .or_else(|| node.child_by_field_name("name"))
12280                .or_else(|| last_named_child(node));
12281            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12282                Some(DeclaratorKind::Variable(node))
12283            } else {
12284                Some(DeclaratorKind::Function(node))
12285            }
12286        }
12287        // A macro marker is declaration provenance, never a declarator: its
12288        // identifier names the macro, not anything the declaration declares.
12289        "macro_modifier" => None,
12290        "init_declarator"
12291        | "pointer_declarator"
12292        | "reference_declarator"
12293        | "parenthesized_declarator"
12294        | "array_declarator"
12295        | "attributed_declarator"
12296        | "template_function" => node
12297            .child_by_field_name("declarator")
12298            .or_else(|| node.child_by_field_name("name"))
12299            .or_else(|| last_named_child(node))
12300            .and_then(classify_declarator),
12301        "identifier" | "field_identifier" | "qualified_identifier" => {
12302            Some(DeclaratorKind::Variable(node))
12303        }
12304        _ => node
12305            .child_by_field_name("declarator")
12306            .or_else(|| node.child_by_field_name("name"))
12307            .or_else(|| last_named_child(node))
12308            .and_then(classify_declarator),
12309    }
12310}
12311
12312fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12313    matches!(
12314        node.kind(),
12315        "function_declarator"
12316            | "init_declarator"
12317            | "pointer_declarator"
12318            | "reference_declarator"
12319            | "parenthesized_declarator"
12320            | "array_declarator"
12321            | "attributed_declarator"
12322            | "template_function"
12323            | "macro_modifier_declarator"
12324            | "identifier"
12325            | "field_identifier"
12326            | "qualified_identifier"
12327    )
12328}
12329
12330fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12331    let class_like = first_class_like_child(node);
12332    let mut cursor = node.walk();
12333    node.named_children(&mut cursor).any(|child| {
12334        matches!(
12335            child.kind(),
12336            "init_declarator"
12337                | "pointer_declarator"
12338                | "reference_declarator"
12339                | "array_declarator"
12340                | "function_declarator"
12341                | "parenthesized_declarator"
12342                | "attributed_declarator"
12343        ) || matches!(
12344            child.kind(),
12345            "identifier" | "field_identifier" | "qualified_identifier"
12346        ) && class_like.is_none_or(|class_node| {
12347            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12348        })
12349    })
12350}
12351
12352/// One namespace-scope forward class declaration that a recovered export-macro
12353/// class definition may borrow its identity from.  Tree-sitter can close a
12354/// malformed class at the enclosing namespace's closing brace, leaving the later
12355/// class definitions as root-level recovered `function_definition` nodes.  A
12356/// preceding `class Name;` in the same namespace is the only structured identity
12357/// signal available in that shape.
12358///
12359/// Everything recorded here is a property of the forward declaration alone.
12360/// What depends on the node doing the asking -- that the forward and its
12361/// namespace both close before it, with nothing but recovery trivia between --
12362/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12363/// tree answers every later question about it.
12364struct CppNamespaceForward {
12365    name: String,
12366    start_byte: usize,
12367    /// Where the malformed namespace that held the forward ended.  No query
12368    /// asks anything else about that node.
12369    namespace_end_byte: usize,
12370    package_name: String,
12371}
12372
12373/// Read `node` as a borrowable namespace forward declaration.
12374///
12375/// The admission is deliberately conservative: only a body-less class specifier
12376/// whose declaration has no declarator, at namespace scope rather than inside a
12377/// function or class body, in a namespace that itself failed to parse.
12378fn cpp_namespace_forward_entry<'tree>(
12379    node: Node<'tree>,
12380    source: &str,
12381    ancestry: &ParentIndex<'tree>,
12382) -> Option<CppNamespaceForward> {
12383    if !matches!(
12384        node.kind(),
12385        "class_specifier" | "struct_specifier" | "union_specifier"
12386    ) || cpp_body_node(node).is_some()
12387    {
12388        return None;
12389    }
12390    let parent = node.parent()?;
12391    if !(parent.kind() == "declaration_list"
12392        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12393    {
12394        return None;
12395    }
12396    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12397    // Borrowing is only justified by the parser-recovery shape we are
12398    // repairing: the namespace that held the forward must itself contain a
12399    // syntax error. A clean, unrelated namespace forward is not an identity
12400    // proof.
12401    if !namespace.has_error() {
12402        return None;
12403    }
12404    Some(CppNamespaceForward {
12405        name: class_like_name(node, source, ancestry)?,
12406        start_byte: node.start_byte(),
12407        namespace_end_byte: namespace.end_byte(),
12408        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12409    })
12410}
12411
12412/// Whether `forward` stands in the recovery relation to the node asking about
12413/// it: it and its malformed namespace both closed before the recovered class,
12414/// and nothing but recovery trivia separates the two.
12415fn cpp_namespace_forward_matches_recovery(
12416    forward: &CppNamespaceForward,
12417    recovered_node: Node<'_>,
12418) -> bool {
12419    forward.start_byte < recovered_node.start_byte()
12420        && forward.namespace_end_byte < recovered_node.start_byte()
12421        && malformed_namespace_is_nearest_recovery_region(
12422            forward.namespace_end_byte,
12423            recovered_node,
12424        )
12425}
12426
12427/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12428/// happen to the declaration set.
12429///
12430/// The recovered set used to be a difference against a clone of the whole
12431/// declaration set, taken once per recovery: O(recoveries x declarations) on
12432/// exactly the error-recovered files that already walk slowest (#2787). The
12433/// walk knows which declarations it creates, so the capture collects them as
12434/// they are made and the difference is never needed.
12435///
12436/// `removed_pre_existing` is what makes that equal to the difference. A
12437/// deferred replacement removes the replaced declaration's children
12438/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12439/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12440/// a removal of a declaration this capture did not create records that it was
12441/// already there when the capture opened.
12442#[derive(Debug, Default)]
12443pub struct CppRecoveryCapture {
12444    /// Declarations created while this capture was open, in creation order.
12445    created: Vec<CodeUnit>,
12446    /// Membership for `created`.
12447    created_units: HashSet<CodeUnit>,
12448    /// Declarations that predate this capture and have been removed during it.
12449    removed_pre_existing: HashSet<CodeUnit>,
12450}
12451
12452/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12453///
12454/// A region reparse is its own tree and is dropped while the walk that made it
12455/// continues, so a later parse can be allocated at the same address and hand out
12456/// the same node ids.  The root's span and shape pin the identity its address
12457/// alone does not: two roots agreeing on all of this are the same parse of the
12458/// same bytes, and a scan of one is a scan of the other.
12459#[derive(PartialEq, Eq, Hash)]
12460pub struct CppTreeIdentity {
12461    root_id: usize,
12462    start_byte: usize,
12463    end_byte: usize,
12464    kind_id: u16,
12465    child_count: usize,
12466}
12467
12468impl CppTreeIdentity {
12469    fn of(root: Node<'_>) -> Self {
12470        Self {
12471            root_id: root.id(),
12472            start_byte: root.start_byte(),
12473            end_byte: root.end_byte(),
12474            kind_id: root.kind_id(),
12475            child_count: root.child_count(),
12476        }
12477    }
12478}
12479
12480/// The namespace forward declarations one tree's prefix holds, folded in as the
12481/// walk asks about them.
12482///
12483/// `scope_for_recovered_exported_class` asks the same question once per
12484/// recovered class, and the answer depends only on the part of the tree that
12485/// starts before the asking node.  Rescanning that prefix per question is
12486/// quadratic in the file, and a generated header whose parse recovery leaves
12487/// thousands of class-like declarations at file scope pays all of it: 1,904
12488/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12489/// (#2754).  This carries the scan forward instead.  Each question advances the
12490/// traversal from wherever the last one stopped to the asking node's start byte,
12491/// so a whole walk pays at most one pass over the prefix its furthest question
12492/// reaches, and each question then costs a name lookup.
12493#[derive(Default)]
12494pub struct CppNamespaceForwardScan {
12495    /// Every named node starting before this byte has been folded in.
12496    scanned_through: usize,
12497    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12498}
12499
12500impl CppNamespaceForwardScan {
12501    /// Fold in every named node of `root` that starts at or after the fold
12502    /// watermark and before `cutoff`.
12503    ///
12504    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12505    /// pass owes are exactly the ones no earlier pass reached, and a question
12506    /// about an earlier byte than one already answered costs nothing.
12507    fn advance_to<'tree>(
12508        &mut self,
12509        root: Node<'tree>,
12510        cutoff: usize,
12511        source: &str,
12512        ancestry: &ParentIndex<'tree>,
12513    ) {
12514        if cutoff <= self.scanned_through {
12515            return;
12516        }
12517        let folded_through = self.scanned_through;
12518        let mut cursor = root.walk();
12519        let mut stack = vec![root];
12520        while let Some(current) = stack.pop() {
12521            if (folded_through..cutoff).contains(&current.start_byte())
12522                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12523            {
12524                self.forwards
12525                    .entry(forward.name.clone())
12526                    .or_default()
12527                    .push(forward);
12528            }
12529            // A subtree ending before the watermark holds only nodes an earlier
12530            // pass already folded, and one starting at or after the cutoff is
12531            // outside the prefix being asked about. Skipping both is what keeps
12532            // the total traversal to one pass.
12533            for child in current.named_children(&mut cursor) {
12534                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12535                    stack.push(child);
12536                }
12537            }
12538        }
12539        self.scanned_through = cutoff;
12540    }
12541
12542    /// The one namespace `name` was forward declared in before `recovered_node`.
12543    /// More than one matching forward declaration is ambiguous and answers
12544    /// nothing rather than guessing.
12545    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12546        let mut matching = self
12547            .forwards
12548            .get(name)
12549            .into_iter()
12550            .flatten()
12551            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12552        let first = matching.next()?;
12553        matching
12554            .next()
12555            .is_none()
12556            .then(|| first.package_name.clone())
12557    }
12558}
12559
12560/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12561/// debug build checks every answer against (and the one the parity tests drive
12562/// directly).  It walks the whole prefix per question, which is exactly the cost
12563/// #2754 removed from the release path.
12564#[cfg(any(debug_assertions, test))]
12565fn unique_earlier_cpp_namespace_forward<'tree>(
12566    recovered_node: Node<'tree>,
12567    name: &str,
12568    source: &str,
12569    ancestry: &ParentIndex<'tree>,
12570) -> Option<String> {
12571    let mut root = recovered_node;
12572    while let Some(parent) = ancestry.parent(root) {
12573        root = parent;
12574    }
12575
12576    let mut candidates = Vec::new();
12577    let mut stack = vec![root];
12578    while let Some(current) = stack.pop() {
12579        if current.start_byte() < recovered_node.start_byte()
12580            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12581            && forward.name == name
12582            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12583        {
12584            candidates.push(forward.package_name);
12585        }
12586
12587        let mut cursor = current.walk();
12588        for child in current.named_children(&mut cursor) {
12589            if child.start_byte() < recovered_node.start_byte() {
12590                stack.push(child);
12591            }
12592        }
12593    }
12594
12595    if candidates.len() == 1 {
12596        candidates.pop()
12597    } else {
12598        None
12599    }
12600}
12601
12602fn malformed_namespace_is_nearest_recovery_region(
12603    namespace_end_byte: usize,
12604    recovered_node: Node<'_>,
12605) -> bool {
12606    let mut root = recovered_node;
12607    while let Some(parent) = root.parent() {
12608        root = parent;
12609    }
12610    let mut cursor = root.walk();
12611    root.named_children(&mut cursor)
12612        .filter(|sibling| {
12613            namespace_end_byte <= sibling.start_byte()
12614                && sibling.end_byte() <= recovered_node.start_byte()
12615        })
12616        .all(is_malformed_namespace_recovery_trivia)
12617}
12618
12619fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12620    matches!(node.kind(), "ERROR" | "comment")
12621        || node.kind().starts_with("preproc_")
12622        || node.kind() == "expression_statement" && node.named_child_count() == 0
12623}
12624
12625/// Return the namespace path for a forward class only when the declaration is
12626/// at namespace scope.  A declaration nested in a function/class body may share
12627/// the same namespace ancestor but cannot identify a top-level class definition.
12628fn cpp_namespace_name_for_forward<'tree>(
12629    node: Node<'tree>,
12630    source: &str,
12631    ancestry: &ParentIndex<'tree>,
12632) -> Option<String> {
12633    cpp_namespace_definition_for_forward(node, ancestry)?;
12634    cpp_lexical_namespace_name(node, source)
12635}
12636
12637fn cpp_namespace_definition_for_forward<'tree>(
12638    node: Node<'tree>,
12639    ancestry: &ParentIndex<'tree>,
12640) -> Option<Node<'tree>> {
12641    let declaration = ancestry.parent(node)?;
12642    let mut ancestor = ancestry.parent(declaration);
12643    while let Some(current) = ancestor {
12644        if matches!(
12645            current.kind(),
12646            "compound_statement"
12647                | "field_declaration_list"
12648                | "class_specifier"
12649                | "struct_specifier"
12650                | "union_specifier"
12651                | "function_definition"
12652                | "lambda_expression"
12653        ) {
12654            return None;
12655        }
12656        if current.kind() == "namespace_definition" {
12657            return Some(current);
12658        }
12659        ancestor = ancestry.parent(current);
12660    }
12661    None
12662}
12663
12664fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12665    match node.kind() {
12666        "template_function" => node
12667            .child_by_field_name("name")
12668            .is_some_and(is_function_pointer_like_inner_declarator),
12669        _ => is_pointer_wrapper_declarator(node),
12670    }
12671}
12672
12673fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12674    match node.kind() {
12675        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12676        "parenthesized_declarator" => node
12677            .child_by_field_name("declarator")
12678            .or_else(|| last_named_child(node))
12679            .is_some_and(is_pointer_wrapper_declarator),
12680        _ => false,
12681    }
12682}
12683
12684/// The canonical owner chain and package of an out-of-line member whose
12685/// written qualifier starts with a name a `using ns::Type;` declaration bound
12686/// in the definition's own namespace scope (#6902 item 4).
12687///
12688/// `using imported::Type; void Type::method() {}` defines
12689/// `imported::Type::method`: the written `Type::` stays in the source, but the
12690/// member's identity follows the binding. The binding proves the owner only
12691/// when every visible binding of that name in this scope names the same
12692/// target. Conflicting bindings, a binding from an enclosing namespace (which
12693/// a same-named class in the inner namespace would shadow), and a using
12694/// declaration this walk cannot see (a preprocessor arm, another file) all
12695/// return `None`, and the caller keeps the written-qualifier reading.
12696fn using_bound_cpp_owner(
12697    declarator_name: Node<'_>,
12698    source: &str,
12699    scope: &ScopeInfo,
12700) -> Option<(Vec<String>, String)> {
12701    if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12702        return None;
12703    }
12704    let (components, explicitly_global) =
12705        structured_cpp_qualified_components(declarator_name, source)?;
12706    let (_, owner) = components.split_last()?;
12707    if explicitly_global
12708        || owner.is_empty()
12709        || components.iter().any(|component| component.is_template_id)
12710    {
12711        return None;
12712    }
12713    let mut targets = scope
12714        .visible_using_declarations
12715        .iter()
12716        .filter(|binding| {
12717            binding.scope_package == scope.package_name && binding.name == owner[0].name
12718        })
12719        .map(|binding| binding.target_package.as_str());
12720    let target_package = targets.next()?;
12721    if targets.any(|other| other != target_package) {
12722        return None;
12723    }
12724    Some((
12725        owner
12726            .iter()
12727            .map(|component| component.name.clone())
12728            .collect(),
12729        target_package.to_string(),
12730    ))
12731}
12732
12733fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12734    let cleaned = raw_name.trim_start_matches("template ").trim();
12735    // A leading `::` is the explicit-global marker, not an empty owner segment.
12736    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12737    // erroneous macro envelope swallowing the first identifier of an
12738    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12739    // split below yields owner_parts `[""]`, constructing a unit with an empty
12740    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12741    let cleaned = cleaned.trim_start_matches("::");
12742    // Parser recovery can preserve two adjacent scope operators around a
12743    // missing component (for example `X::/**/::method` in compiler diagnostic
12744    // fixtures). Empty components are syntax-recovery artifacts, never C++
12745    // owners. Keeping one as the final owner constructed `short_name=".method"`
12746    // and violated the structured package/short boundary during a large LLVM
12747    // workspace build. This is the same legacy-string-to-FqName bridge as the
12748    // ordinary split above; discard only components that the delimiter itself
12749    // proves empty.
12750    let parts: Vec<_> = cleaned
12751        .split("::")
12752        .filter(|component| !component.is_empty())
12753        .collect();
12754    if parts.is_empty() {
12755        return (None, cleaned.to_string(), scope.package_name.clone());
12756    }
12757    if parts.len() > 1 {
12758        let name = parts.last().unwrap_or(&cleaned).to_string();
12759        let owner_parts = &parts[..parts.len() - 1];
12760        if let Some(class_unit) = &scope.class_unit {
12761            // Lexically inside a class body: the owner is that class, whatever
12762            // the declarator re-qualifies it as.
12763            return (
12764                Some(CppMemberOwner::Unit(class_unit.clone())),
12765                name,
12766                scope.package_name.clone(),
12767            );
12768        }
12769        if !scope.package_name.is_empty() {
12770            // Out-of-line member definition written *inside* an enclosing
12771            // `namespace {}` block (scope package is that namespace). Every
12772            // owner segment before the terminal member is a class-nesting step
12773            // -- an out-of-line nested-class member `Outer::Inner::method` in
12774            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12775            // namespace path: `using namespace` never brings nested-class
12776            // access into unqualified scope, so C++ always writes the full
12777            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12778            // that redundantly re-states the enclosing namespace it already
12779            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12780            // strip that re-qualifying prefix (which duplicates a suffix of the
12781            // enclosing package path) before treating what remains as the
12782            // nested-class chain, so the redundant spelling still lands on the
12783            // same `log4cxx.Foo.method` identity as its header declaration.
12784            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12785            let owner = (!nested.is_empty()).then(|| {
12786                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12787            });
12788            return (owner, name, scope.package_name.clone());
12789        }
12790        // File scope (no enclosing `namespace {}` block, scope package empty).
12791        let (owner, package_name) = if owner_parts.len() > 1 {
12792            // A multi-segment qualifier at file scope with no enclosing
12793            // namespace: treat all but the last owner segment as the namespace
12794            // path and the last as the owning class (`ns1::ns2::Class::method`
12795            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12796            // is really a namespace or an outer class cannot be told from the
12797            // declarator text alone here, and no enclosing namespace or
12798            // in-index owner is available at per-file extraction to confirm the
12799            // class reading, so the far-more-common namespace interpretation is
12800            // kept rather than guessed away (the nested-class-at-file-scope and
12801            // using-directive-qualified nested-class shapes remain on this
12802            // behavior; see #1121).
12803            (
12804                Some(CppMemberOwner::Chain(vec![
12805                    owner_parts.last().unwrap_or(&"").to_string(),
12806                ])),
12807                owner_parts[..owner_parts.len() - 1].join("::"),
12808            )
12809        } else {
12810            // A bare `Class::member` qualifier at file scope carries no
12811            // namespace segment of its own. The declarator alone cannot say
12812            // which namespace owns `Class` -- but a `using namespace X;`
12813            // directive already in effect at this point in the file (#1093,
12814            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12815            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12816            // is the remaining structural signal for it, so fall back to it
12817            // rather than leaving the definition's package empty while its
12818            // header declaration (parsed inside the `namespace {}` block) keeps
12819            // the real one -- an identity split that made the same member
12820            // unresolvable under its own displayed spelling.
12821            (
12822                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12823                cpp_using_directive_namespace_for_bare_owner(scope),
12824            )
12825        };
12826        return (owner, name, package_name);
12827    }
12828
12829    let package_name = scope.package_name.clone();
12830    let owner = scope
12831        .class_unit
12832        .as_ref()
12833        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12834    (owner, cleaned.to_string(), package_name)
12835}
12836
12837/// Drop the leading owner segments of an out-of-line member qualifier that
12838/// merely re-state the enclosing namespace the definition already sits in, so
12839/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12840/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12841/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12842/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12843/// noise, not class-nesting steps. Returns the owner segments with the longest
12844/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12845/// names only the enclosing namespace before the terminal member -- a
12846/// re-qualified free function). `package_name` is the enclosing namespace path
12847/// in its stored `::`-joined form; both sides are split on the same delimiter
12848/// the namespace walker joined them with, so this compares namespace *segments*
12849/// rather than scanning text.
12850fn strip_redundant_namespace_prefix<'a>(
12851    owner_parts: &'a [&'a str],
12852    package_name: &str,
12853) -> &'a [&'a str] {
12854    if package_name.is_empty() {
12855        return owner_parts;
12856    }
12857    let package_segments: Vec<&str> = package_name.split("::").collect();
12858    let max_prefix = owner_parts.len().min(package_segments.len());
12859    for prefix_len in (1..=max_prefix).rev() {
12860        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
12861        if &owner_parts[..prefix_len] == package_suffix {
12862            return &owner_parts[prefix_len..];
12863        }
12864    }
12865    owner_parts
12866}
12867
12868/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
12869/// class name at file/namespace scope, from the `using namespace` directives
12870/// visible at this point in the file. Several may be in scope at once (a
12871/// primary `using namespace NS;` alongside deeper conveniences like `using
12872/// namespace NS::helpers;`); since the declarator gives no way to tell which
12873/// one actually declares the owner class, prefer the shallowest (fewest
12874/// `::`-separated segments) as the file's most likely "home" namespace,
12875/// breaking ties by declaration order. Returns an empty string (leaving the
12876/// caller's package unqualified, as before) when no using-namespace directive
12877/// is in scope.
12878fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
12879    scope
12880        .visible_using_namespaces
12881        .iter()
12882        .min_by_key(|namespace| namespace.split("::").count())
12883        .cloned()
12884        .unwrap_or_default()
12885}
12886
12887struct CppQualifiedNameComponent {
12888    name: String,
12889    is_template_id: bool,
12890}
12891
12892/// Canonical nested-class chain for an out-of-line class definition written
12893/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
12894/// component per class (`["Outer", "Inner"]`).
12895///
12896/// The enclosing namespace fixes the namespace/class boundary: after an
12897/// optional redundant spelling of that namespace, every component belongs to
12898/// the class chain. File-scope qualified class names remain untouched because
12899/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
12900///
12901/// The components stay structured (rather than being `$`-joined here) so the
12902/// fq construction can push one Type/Nested segment per class; the `$`-joined
12903/// short-name display form is derived at the call sites that need it.
12904fn qualified_class_name_chain(
12905    class_node: Node<'_>,
12906    source: &str,
12907    scope: &ScopeInfo,
12908) -> Option<Vec<String>> {
12909    if scope.package_name.is_empty() || scope.class_unit.is_some() {
12910        return None;
12911    }
12912    let name = class_node.child_by_field_name("name")?;
12913    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
12914    if explicitly_global
12915        || components.len() < 2
12916        || components.iter().any(|component| component.is_template_id)
12917    {
12918        return None;
12919    }
12920    let names = components
12921        .iter()
12922        .map(|component| component.name.as_str())
12923        .collect::<Vec<_>>();
12924    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
12925    if class_chain.is_empty() {
12926        return None;
12927    }
12928    Some(class_chain.iter().map(|name| name.to_string()).collect())
12929}
12930
12931fn structured_cpp_qualified_components(
12932    qualified_name: Node<'_>,
12933    source: &str,
12934) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
12935    if qualified_name.kind() != "qualified_identifier" {
12936        return None;
12937    }
12938
12939    let mut components = Vec::new();
12940    let mut current = qualified_name;
12941    let mut explicitly_global = false;
12942    loop {
12943        if current.kind() == "qualified_identifier" {
12944            if let Some(component) = current.child_by_field_name("scope") {
12945                components.push(canonical_cpp_qualified_component(component, source)?);
12946            } else if components.is_empty() {
12947                explicitly_global = true;
12948            } else {
12949                return None;
12950            }
12951            current = current.child_by_field_name("name")?;
12952        } else {
12953            components.push(canonical_cpp_qualified_component(current, source)?);
12954            break;
12955        }
12956    }
12957    Some((components, explicitly_global))
12958}
12959
12960fn split_structured_templated_cpp_name(
12961    declarator: Node<'_>,
12962    declarator_name: Node<'_>,
12963    source: &str,
12964    scope: &ScopeInfo,
12965) -> Option<(Option<CppMemberOwner>, String, String)> {
12966    let (mut components, explicitly_global) =
12967        structured_cpp_qualified_components(declarator_name, source)?;
12968
12969    let terminal = components.pop()?;
12970    let owner_start = components
12971        .iter()
12972        .position(|component| component.is_template_id)?;
12973    let explicit_package = components[..owner_start]
12974        .iter()
12975        .map(|component| component.name.as_str())
12976        .collect::<Vec<_>>()
12977        .join("::");
12978    let explicit_package_is_empty = explicit_package.is_empty();
12979    let package_name = match (
12980        explicitly_global,
12981        scope.package_name.is_empty(),
12982        explicit_package_is_empty,
12983    ) {
12984        (true, _, _) => explicit_package,
12985        (false, _, true) => scope.package_name.clone(),
12986        (false, true, false) => explicit_package,
12987        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
12988    };
12989    // Same identity-split fallback as `split_cpp_name` (#1093): a template
12990    // specialization's owner class named with no namespace segment of its own
12991    // (`explicit_package` empty) at file scope (`explicitly_global` false)
12992    // with nothing enclosing (`package_name` still empty) has no structural
12993    // signal for its namespace besides an in-scope `using namespace X;`.
12994    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
12995    {
12996        cpp_using_directive_namespace_for_bare_owner(scope)
12997    } else {
12998        package_name
12999    };
13000    // A specialization member keeps its specialization owner (`Box<T*>`)
13001    // rather than collapsing into the primary's; a qualifier the template
13002    // levels cannot read keeps the bare names.
13003    let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13004        .filter(|names| names.len() == components.len())
13005    {
13006        Some(mut names) => names.split_off(owner_start),
13007        None => components[owner_start..]
13008            .iter()
13009            .map(|component| component.name.clone())
13010            .collect::<Vec<_>>(),
13011    };
13012    if owner_chain.is_empty() || terminal.name.is_empty() {
13013        return None;
13014    }
13015
13016    Some((
13017        Some(CppMemberOwner::Chain(owner_chain)),
13018        terminal.name,
13019        package_name,
13020    ))
13021}
13022
13023fn canonical_cpp_qualified_component(
13024    mut component: Node<'_>,
13025    source: &str,
13026) -> Option<CppQualifiedNameComponent> {
13027    let mut is_template_id = false;
13028    loop {
13029        match component.kind() {
13030            "template_type" => {
13031                is_template_id = true;
13032                component = component.child_by_field_name("name")?;
13033            }
13034            "dependent_name" => component = component.named_child(0)?,
13035            "identifier"
13036            | "field_identifier"
13037            | "namespace_identifier"
13038            | "type_identifier"
13039            | "operator_name"
13040            | "destructor_name" => {
13041                let name = normalize_cpp_whitespace(node_text(component, source));
13042                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13043                    name,
13044                    is_template_id,
13045                });
13046            }
13047            _ => component = component.child_by_field_name("name")?,
13048        }
13049    }
13050}
13051
13052fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13053    if let Some(name) = macro_decorated_unqualified_name(node) {
13054        return extract_declarator_name(name, source);
13055    }
13056    match node.kind() {
13057        "identifier"
13058        | "field_identifier"
13059        | "type_identifier"
13060        | "operator_name"
13061        | "destructor_name"
13062        | "qualified_identifier" => node_text(node, source).to_string(),
13063        "function_declarator"
13064        | "pointer_declarator"
13065        | "reference_declarator"
13066        | "parenthesized_declarator"
13067        | "array_declarator"
13068        | "template_function" => node
13069            .child_by_field_name("declarator")
13070            .or_else(|| node.child_by_field_name("name"))
13071            .or_else(|| last_named_child(node))
13072            .map(|child| extract_declarator_name(child, source))
13073            .unwrap_or_else(|| node_text(node, source).to_string()),
13074        _ => node
13075            .child_by_field_name("name")
13076            .map(|child| extract_declarator_name(child, source))
13077            .unwrap_or_else(|| node_text(node, source).to_string()),
13078    }
13079}
13080
13081/// Extract a callable identity only through declaration-shaped AST nodes.
13082/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
13083/// expose the call's parameter list as a false function declarator; accepting
13084/// arbitrary node text there emitted bogus names such as `.*f`.
13085fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13086    if let Some(name) = macro_decorated_unqualified_name(node) {
13087        return extract_callable_declarator_name(name, source);
13088    }
13089    match node.kind() {
13090        "identifier"
13091        | "field_identifier"
13092        | "type_identifier"
13093        | "operator_name"
13094        | "destructor_name"
13095        | "qualified_identifier" => Some(node_text(node, source).to_string()),
13096        "function_declarator"
13097        | "pointer_declarator"
13098        | "reference_declarator"
13099        | "parenthesized_declarator"
13100        | "array_declarator"
13101        | "template_function" => node
13102            .child_by_field_name("declarator")
13103            .or_else(|| node.child_by_field_name("name"))
13104            .and_then(|child| extract_callable_declarator_name(child, source)),
13105        _ => None,
13106    }
13107}
13108
13109fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13110    loop {
13111        if let Some(name) = macro_decorated_unqualified_name(node) {
13112            node = name;
13113            continue;
13114        }
13115        match node.kind() {
13116            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13117                let name = node_text(node, source).trim().to_string();
13118                return (!name.is_empty()).then_some(name);
13119            }
13120            _ => {
13121                node = node
13122                    .child_by_field_name("declarator")
13123                    .or_else(|| node.child_by_field_name("name"))
13124                    .or_else(|| last_named_child(node))?;
13125            }
13126        }
13127    }
13128}
13129
13130/// Recover a C field whose name is wrapped in the function-like
13131/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
13132/// function declarator, while retaining its argument as a structured child.
13133/// The exact macro name and one-argument shape keep ordinary function-pointer
13134/// fields and unknown malformed declarators fail-closed.
13135#[derive(Clone, Copy)]
13136pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13137    pub(crate) name: Node<'tree>,
13138    pub(crate) declarator: Node<'tree>,
13139}
13140
13141impl RecoveredFunctionLikeFieldDeclarator<'_> {
13142    pub(crate) fn pointer_depth(self) -> i32 {
13143        let mut depth = 0;
13144        let mut current = self.declarator;
13145        while current.kind() != "function_declarator" {
13146            if current.kind() == "pointer_declarator" {
13147                depth += 1;
13148            }
13149            current = current
13150                .child_by_field_name("declarator")
13151                .expect("recovered field wrapper has an inner declarator");
13152        }
13153        depth
13154    }
13155}
13156
13157/// Whether a class-scope field declaration is really a function-like macro
13158/// invocation whose replacement the parser never saw.
13159///
13160/// The invocation carries no terminator of its own, so tree-sitter invents a
13161/// zero-width `;` and reads the macro name as the member's type and the
13162/// argument list as a `parenthesized_declarator`. That shape is not a member:
13163/// a member terminates itself, and with a real terminator the grammar reads
13164/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
13165/// type can name the macro being invoked; a builtin type keeps whatever
13166/// recovery the ordinary declarator walk makes of it.
13167fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13168    if node.kind() != "field_declaration" {
13169        return false;
13170    }
13171    let Some(type_node) = node.child_by_field_name("type") else {
13172        return false;
13173    };
13174    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13175        return false;
13176    }
13177    let Some(declarator) = node.child_by_field_name("declarator") else {
13178        return false;
13179    };
13180    if declarator.kind() != "parenthesized_declarator" {
13181        return false;
13182    }
13183    let mut invented_terminator = false;
13184    for child in children_iter(node) {
13185        if child.kind() != ";" {
13186            continue;
13187        }
13188        if !child.is_missing() {
13189            return false;
13190        }
13191        invented_terminator = true;
13192    }
13193    invented_terminator
13194}
13195
13196/// An access label that a class-scope function-like macro invocation
13197/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
13198pub(crate) struct CppSwallowedAccessLabel<'tree> {
13199    /// The access keyword, which the grammar read as a `field_identifier`.
13200    pub(crate) keyword: Node<'tree>,
13201    /// `"public"`, `"protected"` or `"private"`.
13202    pub(crate) access: &'static str,
13203}
13204
13205/// Whether a class-body member is a function-like macro invocation that
13206/// swallowed the access label written after it, and with it the members under
13207/// that label.
13208///
13209/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
13210/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
13211/// the invocation as a `macro_type_specifier`, the `public` after it as the
13212/// member's `field_identifier` declarator, and the label's `:` as a bitfield
13213/// whose width expression shreds the next member, so the constructor written
13214/// under the label is left with no declarator at all. An access keyword is
13215/// never a declarator name, and a bitfield after it is how the parser spent
13216/// the label's colon, so the shape is the evidence. The access ladders read
13217/// the label from it too, because it still applies to every member after it.
13218pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13219    node: Node<'tree>,
13220    source: &str,
13221) -> Option<CppSwallowedAccessLabel<'tree>> {
13222    if !matches!(node.kind(), "field_declaration" | "function_definition") {
13223        return None;
13224    }
13225    node.child_by_field_name("type")
13226        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13227    let keyword = node
13228        .child_by_field_name("declarator")
13229        .filter(|declarator| declarator.kind() == "field_identifier")?;
13230    let access = match node_text(keyword, source) {
13231        "public" => "public",
13232        "protected" => "protected",
13233        "private" => "private",
13234        _ => return None,
13235    };
13236    let mut clause = keyword.next_sibling()?;
13237    if clause.kind() == "ERROR" {
13238        clause = clause.child(0)?;
13239    }
13240    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13241        return None;
13242    }
13243    Some(CppSwallowedAccessLabel { keyword, access })
13244}
13245
13246pub(crate) fn recovered_function_like_field_declarator<'tree>(
13247    node: Node<'tree>,
13248    source: &str,
13249) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13250    if node.kind() != "field_declaration" {
13251        return None;
13252    }
13253    let outer_declarator = node.child_by_field_name("declarator")?;
13254    let mut declarator = outer_declarator;
13255    while matches!(
13256        declarator.kind(),
13257        "pointer_declarator"
13258            | "reference_declarator"
13259            | "array_declarator"
13260            | "parenthesized_declarator"
13261    ) {
13262        declarator = declarator.child_by_field_name("declarator")?;
13263    }
13264    if declarator.kind() != "function_declarator" {
13265        return None;
13266    }
13267    let macro_name = declarator.child_by_field_name("declarator")?;
13268    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13269    {
13270        return None;
13271    }
13272    let parameters = declarator.child_by_field_name("parameters")?;
13273    let mut cursor = parameters.walk();
13274    let mut arguments = parameters.named_children(&mut cursor);
13275    let parameter = arguments.next()?;
13276    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13277        return None;
13278    }
13279    let name = parameter.child_by_field_name("type").filter(|argument| {
13280        matches!(
13281            argument.kind(),
13282            "identifier" | "field_identifier" | "type_identifier"
13283        )
13284    })?;
13285    Some(RecoveredFunctionLikeFieldDeclarator {
13286        name,
13287        declarator: outer_declarator,
13288    })
13289}
13290
13291use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13292
13293fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13294    let name_node = node.child_by_field_name("name")?;
13295    let name = normalize_cpp_whitespace(node_text(name_node, source));
13296    (!name.is_empty()).then_some(name)
13297}
13298
13299fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13300    if node.kind() != "declaration" {
13301        return Vec::new();
13302    }
13303    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13304    // or -- under the macro-declaration grammar, which reads the keyword as
13305    // an object-like macro -- the identifier a leading `macro_modifier`
13306    // wraps, with the aliased type in the type field.
13307    let keyword_is_alias = node
13308        .child_by_field_name("type")
13309        .filter(|node| {
13310            node.kind() == "type_identifier"
13311                && matches!(node_text(*node, source), "using" | "typedef")
13312        })
13313        .map(|keyword| node_text(keyword, source).to_owned())
13314        .or_else(|| {
13315            node.named_children(&mut node.walk())
13316                .find_map(macro_modifier_identifier)
13317                .map(|identifier| node_text(identifier, source).to_owned())
13318                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13319        });
13320    let Some(keyword_is_alias) = keyword_is_alias else {
13321        return Vec::new();
13322    };
13323    let Some(declarator) = node.child_by_field_name("declarator") else {
13324        return Vec::new();
13325    };
13326    if keyword_is_alias == "using"
13327        && (declarator.kind() != "init_declarator"
13328            || declarator.child_by_field_name("value").is_none())
13329    {
13330        return Vec::new();
13331    }
13332    if keyword_is_alias == "typedef"
13333        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13334    {
13335        return vec![alias_name];
13336    }
13337    extract_typedef_declarator_name(declarator, source)
13338        .into_iter()
13339        .collect()
13340}
13341
13342/// The RHS retained by the same malformed alias shapes declaration admission
13343/// accepts. The alias name proves which declarator owns this source evidence.
13344pub(crate) fn recovered_alias_type_node<'tree>(
13345    node: Node<'tree>,
13346    alias_name: &str,
13347    source: &str,
13348) -> Option<Node<'tree>> {
13349    if node.kind() != "declaration" {
13350        return None;
13351    }
13352    let declarator = node.child_by_field_name("declarator")?;
13353    let admitted = recovered_type_alias_names(node, source)
13354        .iter()
13355        .any(|name| name == alias_name)
13356        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13357    if !admitted {
13358        return None;
13359    }
13360    if declarator.kind() == "init_declarator" {
13361        return declarator.child_by_field_name("value");
13362    }
13363    // The macro-declaration grammar keeps the aliased type in the type field
13364    // and the keyword as a `macro_modifier`; the alias name is the
13365    // declarator, so the type field is the RHS evidence.
13366    if node
13367        .named_children(&mut node.walk())
13368        .find_map(macro_modifier_identifier)
13369        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13370    {
13371        return node.child_by_field_name("type");
13372    }
13373    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13374        .then_some(declarator)
13375}
13376
13377fn recovered_typedef_error_alias_name(
13378    declaration: Node<'_>,
13379    declarator: Node<'_>,
13380    source: &str,
13381) -> Option<String> {
13382    // An export macro between `class` and its name can make tree-sitter parse
13383    // the recovered class body as a function body. In that shape,
13384    //
13385    //     typedef spi::Filter BASE_CLASS;
13386    //
13387    // becomes a declaration whose `declarator` is the underlying qualified
13388    // type (`spi::Filter`) and whose actual alias name is displaced into the
13389    // following ERROR node. Do not publish the terminal underlying type
13390    // (`Filter`) as a false class-owned alias.
13391    if declarator.kind() != "qualified_identifier" {
13392        return None;
13393    }
13394    let mut cursor = declaration.walk();
13395    let mut errors = declaration
13396        .named_children(&mut cursor)
13397        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13398    let error = errors.next()?;
13399    if errors.next().is_some() || error.named_child_count() != 1 {
13400        return None;
13401    }
13402    let name = error.named_child(0)?;
13403    if !matches!(
13404        name.kind(),
13405        "identifier" | "field_identifier" | "type_identifier"
13406    ) {
13407        return None;
13408    }
13409    let name = normalize_cpp_whitespace(node_text(name, source));
13410    (!name.is_empty()).then_some(name)
13411}
13412
13413fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13414    // A function-like token in the type position can make tree-sitter expose
13415    // its argument as a parenthesized declarator. Do not publish that argument
13416    // as an alias. The macro-specific recovery below handles the proven shape.
13417    if fragmented_parenthesized_typedef_type(node).is_some() {
13418        return Vec::new();
13419    }
13420    let has_function_like_macro_type = node
13421        .child_by_field_name("type")
13422        .filter(|type_node| type_node.kind() == "type_identifier")
13423        .is_some_and(|type_node| {
13424            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13425        });
13426    let mut names = Vec::new();
13427    let mut cursor = node.walk();
13428    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13429        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13430            continue;
13431        }
13432        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13433            && !names.contains(&name)
13434        {
13435            names.push(name);
13436        }
13437    }
13438    names
13439}
13440
13441struct RecoveredMacroTypedefAlias<'tree> {
13442    name: String,
13443    end_node: Node<'tree>,
13444}
13445
13446/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13447/// into an identifier expression statement. The uppercase macro token, missing
13448/// typedef terminator, and complete sibling terminator prove this exact shape.
13449fn recovered_macro_typedef_alias<'tree>(
13450    node: Node<'tree>,
13451    source: &str,
13452) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13453    let type_node = fragmented_parenthesized_typedef_type(node)?;
13454    if type_node.kind() != "type_identifier"
13455        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13456    {
13457        return None;
13458    }
13459
13460    let end_node = node.next_named_sibling()?;
13461    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13462        return None;
13463    }
13464    let name_node = end_node.named_child(0)?;
13465    if name_node.kind() != "identifier" {
13466        return None;
13467    }
13468    let has_terminator = (0..end_node.child_count()).any(|index| {
13469        end_node
13470            .child(index)
13471            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13472    });
13473    if !has_terminator {
13474        return None;
13475    }
13476    let name = normalize_cpp_whitespace(node_text(name_node, source));
13477    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13478}
13479
13480fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13481    if node.kind() != "type_definition" {
13482        return None;
13483    }
13484    let mut declarator_cursor = node.walk();
13485    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13486    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13487        return None;
13488    }
13489    let has_missing_terminator = (0..node.child_count()).any(|index| {
13490        node.child(index)
13491            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13492    });
13493    if !has_missing_terminator {
13494        return None;
13495    }
13496    node.child_by_field_name("type")
13497}
13498
13499pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13500    let mut stack = vec![node];
13501    while let Some(node) = stack.pop() {
13502        let child = match node.kind() {
13503            "identifier" | "field_identifier" | "type_identifier" => {
13504                return direct_identifier_name(node, source);
13505            }
13506            "qualified_identifier" => node.child_by_field_name("name")?,
13507            _ => node
13508                .child_by_field_name("declarator")
13509                .or_else(|| node.child_by_field_name("name"))
13510                .or_else(|| last_named_child(node))?,
13511        };
13512        stack.push(child);
13513    }
13514    None
13515}
13516
13517fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13518    if let Some(name_node) = node.child_by_field_name("name") {
13519        let name = normalize_cpp_whitespace(node_text(name_node, source));
13520        return (!name.is_empty()).then_some(name);
13521    }
13522    let mut cursor = node.walk();
13523    node.named_children(&mut cursor)
13524        .find(|child| {
13525            matches!(
13526                child.kind(),
13527                "identifier" | "field_identifier" | "type_identifier"
13528            )
13529        })
13530        .and_then(|name_node| direct_identifier_name(name_node, source))
13531}
13532
13533fn render_cpp_type_signature(
13534    node: Node<'_>,
13535    source: &str,
13536    template_signature: Option<&str>,
13537) -> String {
13538    let text = normalize_cpp_whitespace(node_text(node, source));
13539    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13540    let rendered = if head.ends_with(';') {
13541        head.to_string()
13542    } else {
13543        format!("{head} {{")
13544    };
13545    if let Some(template_signature) = template_signature {
13546        format!("template {template_signature} {rendered}")
13547    } else {
13548        rendered
13549    }
13550}
13551
13552fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13553    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13554        && recovered.declarator == declarator
13555    {
13556        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13557        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13558        return format!("{type_text} {declarator};");
13559    }
13560    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13561        && recovered.name == declarator
13562    {
13563        let type_text = node
13564            .child_by_field_name("type")
13565            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13566            .unwrap_or_default();
13567        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13568        let mut prefix = String::new();
13569        let mut suffix = String::new();
13570        let mut current = recovered.declarator;
13571        while current.kind() != "function_declarator" {
13572            match current.kind() {
13573                "pointer_declarator" => prefix.push('*'),
13574                "reference_declarator" => prefix.push('&'),
13575                "array_declarator" => {
13576                    let size = current
13577                        .child_by_field_name("size")
13578                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13579                        .unwrap_or_default();
13580                    suffix.push('[');
13581                    suffix.push_str(&size);
13582                    suffix.push(']');
13583                }
13584                "parenthesized_declarator" => {}
13585                _ => unreachable!("validated recovered field declarator wrapper"),
13586            }
13587            current = current
13588                .child_by_field_name("declarator")
13589                .expect("recovered field wrapper has an inner declarator");
13590        }
13591        let separator = if prefix.is_empty() { "" } else { " " };
13592        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13593    }
13594    if let Some(signature) =
13595        render_recovered_macro_qualified_field_signature(node, declarator, source)
13596    {
13597        return signature;
13598    }
13599    let prefix = cpp_declaration_prefix(node, source);
13600    let name = extract_variable_name(declarator, source).unwrap_or_default();
13601    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13602    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13603        || (prefix.ends_with('&') && raw_suffix == "&")
13604    {
13605        String::new()
13606    } else {
13607        raw_suffix
13608    };
13609
13610    let mut rendered = if suffix.is_empty() {
13611        format!("{prefix} {name}")
13612    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13613        format!("{prefix}{suffix} {name}")
13614    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13615        format!("{prefix} {name}{suffix}")
13616    } else {
13617        format!("{prefix} {suffix}{name}")
13618    };
13619    rendered = collapse_cpp_whitespace(&rendered);
13620
13621    // Check the terminal token directly instead of normalizing the whole
13622    // declaration, which can copy a large initializer for each field.
13623    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13624        format!("{rendered} = {initializer};")
13625    } else if cpp_direct_semicolon(node).is_some() {
13626        format!("{rendered};")
13627    } else {
13628        rendered
13629    }
13630}
13631
13632fn render_recovered_macro_qualified_field_signature(
13633    node: Node<'_>,
13634    declarator: Node<'_>,
13635    source: &str,
13636) -> Option<String> {
13637    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13638    if !recovered
13639        .iter()
13640        .any(|candidate| same_node(*candidate, declarator))
13641    {
13642        return None;
13643    }
13644    let pseudo_declarator = node.child_by_field_name("declarator")?;
13645    let mut cursor = node.walk();
13646    let clause = node
13647        .named_children(&mut cursor)
13648        .find(|child| child.kind() == "bitfield_clause")?;
13649    let mut cursor = clause.walk();
13650    let error = clause
13651        .named_children(&mut cursor)
13652        .find(|child| child.kind() == "ERROR")?;
13653    let qualified_type =
13654        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13655    let prefix = cpp_declaration_prefix(node, source);
13656    let name = extract_variable_name(declarator, source)?;
13657    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13658    let mut rendered = if suffix.is_empty() {
13659        format!("{prefix} {qualified_type} {name}")
13660    } else {
13661        format!("{prefix} {qualified_type} {suffix} {name}")
13662    };
13663    rendered = collapse_cpp_whitespace(&rendered);
13664
13665    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13666        Some(format!(
13667            "{rendered} = {};",
13668            normalize_cpp_whitespace(node_text(initializer, source))
13669        ))
13670    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13671        Some(format!("{rendered} = {initializer};"))
13672    } else {
13673        Some(format!("{rendered};"))
13674    }
13675}
13676
13677fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13678    match node.kind() {
13679        "pointer_expression" => {
13680            let operator = node
13681                .child_by_field_name("operator")
13682                .or_else(|| node.child(0))
13683                .map(|operator| node_text(operator, source))
13684                .unwrap_or("*");
13685            let argument = node
13686                .child_by_field_name("argument")
13687                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13688                .unwrap_or_default();
13689            format!("{operator}{argument}")
13690        }
13691        "unary_expression" => {
13692            let operator = node
13693                .child_by_field_name("operator")
13694                .or_else(|| node.child(0))
13695                .map(|operator| node_text(operator, source))
13696                .unwrap_or_default();
13697            let argument = node
13698                .child_by_field_name("argument")
13699                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13700                .unwrap_or_default();
13701            format!("{operator}{argument}")
13702        }
13703        "identifier" | "field_identifier" => String::new(),
13704        _ => cpp_declarator_suffix_without_name(node, source),
13705    }
13706}
13707
13708fn recovered_macro_qualified_field_initializer<'tree>(
13709    clause: Node<'tree>,
13710    declarator: Node<'tree>,
13711) -> Option<Node<'tree>> {
13712    let mut stack = vec![clause];
13713    while let Some(current) = stack.pop() {
13714        if current.kind() == "assignment_expression"
13715            && current
13716                .child_by_field_name("left")
13717                .is_some_and(|left| same_node(left, declarator))
13718        {
13719            return current.child_by_field_name("right");
13720        }
13721        let mut cursor = current.walk();
13722        stack.extend(current.named_children(&mut cursor));
13723    }
13724    None
13725}
13726
13727fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13728    let text = node_text(node, source);
13729    let mut cursor = node.walk();
13730    let first_declarator = node.named_children(&mut cursor).find(|child| {
13731        matches!(
13732            child.kind(),
13733            "init_declarator"
13734                | "identifier"
13735                | "field_identifier"
13736                | "pointer_declarator"
13737                | "reference_declarator"
13738                | "array_declarator"
13739                | "function_declarator"
13740        )
13741    });
13742    let prefix = if let Some(first_declarator) = first_declarator {
13743        let end = first_declarator
13744            .start_byte()
13745            .saturating_sub(node.start_byte());
13746        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13747        let declarator_suffix = match first_declarator.kind() {
13748            "init_declarator" => first_declarator
13749                .child_by_field_name("declarator")
13750                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13751                .unwrap_or_default(),
13752            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13753        };
13754        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13755            prefix.push_str(&declarator_suffix);
13756        }
13757        return collapse_cpp_whitespace(&prefix)
13758            .trim_end_matches(',')
13759            .trim_end_matches(';')
13760            .trim()
13761            .to_string();
13762    } else {
13763        text
13764    };
13765    collapse_cpp_whitespace(prefix)
13766        .trim_end_matches(',')
13767        .trim_end_matches(';')
13768        .trim()
13769        .to_string()
13770}
13771
13772fn cpp_preserved_initializer(
13773    declaration_node: Node<'_>,
13774    declarator: Node<'_>,
13775    source: &str,
13776) -> Option<String> {
13777    let name = extract_variable_name(declarator, source)?;
13778    let mut cursor = declaration_node.walk();
13779    for child in declaration_node.named_children(&mut cursor) {
13780        if child.kind() != "init_declarator" {
13781            continue;
13782        }
13783        let Some(inner) = child.child_by_field_name("declarator") else {
13784            continue;
13785        };
13786        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13787            continue;
13788        }
13789        let value = child.child_by_field_name("value")?;
13790        let kind = value.kind();
13791        if matches!(
13792            kind,
13793            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13794        ) {
13795            return Some(normalize_cpp_whitespace(node_text(value, source)));
13796        }
13797        break;
13798    }
13799    // The current grammar keeps a member initializer as a `default_value`
13800    // field beside the declarator rather than inside an `init_declarator`.
13801    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13802        && let Some(value) = equals.next_sibling().filter(|node| {
13803            declaration_node
13804                .child_by_field_name("default_value")
13805                .is_some_and(|default| same_node(default, *node))
13806        })
13807    {
13808        let kind = value.kind();
13809        if matches!(
13810            kind,
13811            "number_literal"
13812                | "float_literal"
13813                | "char_literal"
13814                | "true"
13815                | "false"
13816                | "null"
13817                | "nullptr"
13818                | "identifier"
13819        ) {
13820            return Some(normalize_cpp_whitespace(node_text(value, source)));
13821        }
13822    }
13823    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13824    let pattern = format!(
13825        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13826        regex::escape(&name)
13827    );
13828    Regex::new(&pattern)
13829        .ok()
13830        .and_then(|regex| regex.captures(&declaration_text))
13831        .and_then(|captures| captures.get(1))
13832        .map(|value| value.as_str().to_string())
13833}
13834
13835fn render_cpp_function_display_signature_from_node<'tree>(
13836    node: Node<'tree>,
13837    source: &str,
13838    template_signature: Option<&str>,
13839    has_body: bool,
13840    ancestry: &ParentIndex<'tree>,
13841) -> String {
13842    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13843    let parent_text = node_text(root, source);
13844    let body_local_start = root
13845        .child_by_field_name("body")
13846        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13847        .unwrap_or(parent_text.len());
13848    let display = parent_text
13849        .get(..body_local_start)
13850        .unwrap_or(parent_text)
13851        .trim()
13852        .trim();
13853    let display = if let Some(template_signature) = template_signature {
13854        if display.starts_with("template ") {
13855            display.to_string()
13856        } else {
13857            format!("template {template_signature} {display}")
13858        }
13859    } else {
13860        display.to_string()
13861    };
13862    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
13863    if has_body {
13864        format!("{display} {{...}}")
13865    } else {
13866        format!("{display};")
13867    }
13868}
13869
13870fn cpp_template_signature(
13871    template_node: Node<'_>,
13872    declaration_child: Node<'_>,
13873    source: &str,
13874) -> Option<String> {
13875    let text = source
13876        .get(template_node.start_byte()..declaration_child.start_byte())
13877        .unwrap_or("");
13878    let text = normalize_cpp_whitespace(text);
13879    let start = text.find('<')?;
13880    let end = text.rfind('>')?;
13881    if end < start {
13882        return None;
13883    }
13884    Some(text[start..=end].to_string())
13885}
13886
13887struct RecoveredFragmentedPartialSpecialization<'tree> {
13888    declaration_node: Node<'tree>,
13889    name: String,
13890    range: Range,
13891    prefix_members: Vec<Node<'tree>>,
13892    member_siblings: Vec<Node<'tree>>,
13893    following_declarations: Vec<Node<'tree>>,
13894}
13895
13896struct RecoveredFragmentedPreprocessorClass<'tree> {
13897    declaration_node: Node<'tree>,
13898    class_node: Node<'tree>,
13899    body: Node<'tree>,
13900    name: String,
13901    range: Range,
13902    tail_members: Vec<Node<'tree>>,
13903    member_siblings: Vec<Node<'tree>>,
13904}
13905
13906/// Recover a class whose preprocessor-fragmented parse closes at an early
13907/// member body and publishes the remaining in-class declarations as siblings
13908/// of the surrounding alternative. Primary classes are admitted only when an
13909/// earlier branch contains the matching bodyless declaration and the class
13910/// node retains the displaced `#endif`. Partial specializations instead carry
13911/// their identity structurally in the `template_type` name and template
13912/// metadata. Retain the original AST nodes and re-own only the siblings through
13913/// the displaced structural `};` terminator.
13914fn recover_fragmented_preprocessor_class<'tree>(
13915    template_node: Node<'tree>,
13916    source: &str,
13917    ancestry: &ParentIndex<'tree>,
13918) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
13919    let alternative = ancestry.parent(template_node)?;
13920    if alternative.kind() != "preproc_else" {
13921        return None;
13922    }
13923    let conditional = alternative.parent()?;
13924    if conditional.kind() != "preproc_if" {
13925        return None;
13926    }
13927    let declaration_node = template_node
13928        .named_children(&mut template_node.walk())
13929        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
13930    let class_node = declaration_node
13931        .named_children(&mut declaration_node.walk())
13932        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
13933    let body = cpp_body_node(class_node)?;
13934    if class_node.end_byte() >= declaration_node.end_byte() {
13935        return None;
13936    }
13937    let name = class_like_name(class_node, source, ancestry)?;
13938    let is_partial_specialization = class_node
13939        .child_by_field_name("name")
13940        .is_some_and(|class_name| class_name.kind() == "template_type");
13941    if is_partial_specialization {
13942        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
13943        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
13944            return None;
13945        }
13946    } else {
13947        if !class_has_displaced_preprocessor_terminator(class_node) {
13948            return None;
13949        }
13950        let matching_other_branch = conditional
13951            .named_children(&mut conditional.walk())
13952            .take_while(|child| !same_node(*child, alternative))
13953            .filter(|child| child.kind() == "template_declaration")
13954            .filter_map(first_class_like_child)
13955            .any(|candidate| {
13956                cpp_body_node(candidate).is_none()
13957                    && class_like_name(candidate, source, ancestry).as_deref()
13958                        == Some(name.as_str())
13959            });
13960        if !matching_other_branch {
13961            return None;
13962        }
13963    }
13964
13965    let mut tail_members = Vec::new();
13966    let mut saw_class = false;
13967    let mut declaration_cursor = declaration_node.walk();
13968    for child in declaration_node.named_children(&mut declaration_cursor) {
13969        if same_node(child, class_node) {
13970            saw_class = true;
13971        } else if saw_class {
13972            tail_members.push(child);
13973        }
13974    }
13975
13976    let mut member_siblings = Vec::new();
13977    let mut saw_template = false;
13978    let mut terminator = None;
13979    for index in 0..alternative.child_count() {
13980        let Some(child) = alternative.child(index) else {
13981            continue;
13982        };
13983        if same_node(child, template_node) {
13984            saw_template = true;
13985            continue;
13986        }
13987        if !saw_template {
13988            continue;
13989        }
13990        if displaced_fragmented_class_terminator(alternative, index) {
13991            terminator = alternative.child(index + 1);
13992            break;
13993        }
13994        if child.is_named() {
13995            member_siblings.push(child);
13996        }
13997    }
13998    let terminator = terminator?;
13999    Some(RecoveredFragmentedPreprocessorClass {
14000        declaration_node,
14001        class_node,
14002        body,
14003        name,
14004        range: Range {
14005            start_byte: class_node.start_byte(),
14006            end_byte: terminator.end_byte(),
14007            start_line: class_node.start_position().row + 1,
14008            end_line: terminator.end_position().row + 1,
14009        },
14010        tail_members,
14011        member_siblings,
14012    })
14013}
14014
14015fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14016    (0..class_node.child_count()).any(|index| {
14017        class_node.child(index).is_some_and(|child| {
14018            child.kind() == "ERROR"
14019                && (0..child.child_count()).any(|error_index| {
14020                    child
14021                        .child(error_index)
14022                        .is_some_and(|token| token.kind() == "#endif")
14023                })
14024        })
14025    })
14026}
14027
14028/// The real `#endif` that tree-sitter consumed inside an error subtree.
14029///
14030/// A preprocessor directive inside a malformed array bound can cause later
14031/// declarations to remain children of the conditional. The non-missing token
14032/// still gives the exact structured boundary. Ignore nested conditionals and
14033/// select the last unpaired error-owned token. Tree-sitter can pair a later
14034/// outer `#endif` with this conditional, so the direct terminator is not
14035/// necessarily missing.
14036pub fn cpp_displaced_preprocessor_terminator<'tree>(
14037    conditional: Node<'tree>,
14038) -> Option<Node<'tree>> {
14039    if !conditional.has_error() {
14040        return None;
14041    }
14042    let has_concrete_direct_terminator = conditional
14043        .child_count()
14044        .checked_sub(1)
14045        .and_then(|index| conditional.child(index))
14046        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14047    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14048        // A structured alternative proves that the direct `#endif` closes
14049        // this family. An error-owned terminator inside either branch belongs
14050        // to a damaged nested conditional, not to this one.
14051        return None;
14052    }
14053    // Pair concrete directive tokens while walking the recovered subtree. An
14054    // inner conditional may be represented only by its `#ifndef`/`#endif`
14055    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
14056    // sufficient: an inner `#endif` can otherwise be mistaken for this
14057    // conditional's displaced terminator.
14058    let mut displaced = None;
14059    let mut conditional_depth = 0usize;
14060    let mut stack = children_iter(conditional)
14061        .map(|child| (child, false))
14062        .collect::<Vec<_>>();
14063    stack.reverse();
14064    while let Some((node, inside_error)) = stack.pop() {
14065        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14066            continue;
14067        }
14068        if node != conditional
14069            && matches!(
14070                node.kind(),
14071                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14072            )
14073        {
14074            continue;
14075        }
14076        let inside_error = inside_error || node.kind() == "ERROR";
14077        match node.kind() {
14078            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14079                conditional_depth += 1;
14080            }
14081            "#endif" if !node.is_missing() => {
14082                if conditional_depth == 0
14083                    && inside_error
14084                    && displaced
14085                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14086                {
14087                    displaced = Some(node);
14088                }
14089                conditional_depth = conditional_depth.saturating_sub(1);
14090            }
14091            _ => {}
14092        }
14093        let first_pushed = stack.len();
14094        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14095        stack[first_pushed..].reverse();
14096    }
14097    displaced
14098}
14099
14100/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
14101/// an `ERROR` inside the conditional's own branch.
14102///
14103/// The macro-declaration grammar can read an unterminated end sentinel, the
14104/// branch directive and the next branch's begin sentinel as one declaration
14105/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
14106/// `ERROR` child and the conditional loses its `alternative`. The token is
14107/// still the structured branch boundary. Nested conditionals own their own
14108/// directives, including ones represented only by error-owned `#if`/`#endif`
14109/// tokens, so the walk pairs those the same way
14110/// [`cpp_displaced_preprocessor_terminator`] does.
14111pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14112    if !conditional.has_error() {
14113        return None;
14114    }
14115    let alternative = conditional.child_by_field_name("alternative");
14116    let mut conditional_depth = 0usize;
14117    let mut stack = children_iter(conditional)
14118        .filter(|child| Some(*child) != alternative)
14119        .map(|child| (child, false))
14120        .collect::<Vec<_>>();
14121    stack.reverse();
14122    while let Some((node, inside_error)) = stack.pop() {
14123        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14124            continue;
14125        }
14126        if matches!(
14127            node.kind(),
14128            "preproc_if"
14129                | "preproc_ifdef"
14130                | "preproc_ifndef"
14131                | "preproc_elif"
14132                | "preproc_elifdef"
14133                | "preproc_else"
14134        ) {
14135            continue;
14136        }
14137        let inside_error = inside_error || node.kind() == "ERROR";
14138        match node.kind() {
14139            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14140            "#endif" if !node.is_missing() => {
14141                conditional_depth = conditional_depth.saturating_sub(1);
14142            }
14143            "#else" | "#elif" | "#elifdef" | "#elifndef"
14144                if inside_error && conditional_depth == 0 && !node.is_missing() =>
14145            {
14146                return Some(node);
14147            }
14148            _ => {}
14149        }
14150        let first_pushed = stack.len();
14151        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14152        stack[first_pushed..].reverse();
14153    }
14154    None
14155}
14156
14157/// The effective end of a conditional whose real terminator tree-sitter
14158/// displaced into declaration recovery.
14159///
14160/// Most damaged conditionals retain a concrete `#endif` token below an
14161/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
14162/// boundary. A preprocessor family that selects the middle of a declaration
14163/// can lose the directive tokens entirely. In that shape tree-sitter leaves
14164/// the declaration's `typedef` token as the sole child of the immediately
14165/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
14166/// declarator name inside the conditional's first declaration. The declaration
14167/// end is then the smallest structured boundary that contains the whole split
14168/// declaration.
14169#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14170pub struct CppDisplacedPreprocessorBoundary {
14171    pub end_byte: usize,
14172    pub end_line: usize,
14173}
14174
14175pub fn cpp_displaced_preprocessor_boundary(
14176    conditional: Node<'_>,
14177) -> Option<CppDisplacedPreprocessorBoundary> {
14178    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14179        return Some(CppDisplacedPreprocessorBoundary {
14180            end_byte: terminator.end_byte(),
14181            end_line: terminator.end_position().row + 1,
14182        });
14183    }
14184    if let Some(declaration) = displaced_split_declaration(conditional) {
14185        return Some(CppDisplacedPreprocessorBoundary {
14186            end_byte: declaration.end_byte(),
14187            end_line: declaration.end_position().row + 1,
14188        });
14189    }
14190    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14191        return Some(CppDisplacedPreprocessorBoundary {
14192            end_byte: terminator.end_byte(),
14193            end_line: terminator.end_position().row + 1,
14194        });
14195    }
14196    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14197        return Some(CppDisplacedPreprocessorBoundary {
14198            end_byte: terminator.end_byte(),
14199            end_line: terminator.end_position().row + 1,
14200        });
14201    }
14202    None
14203}
14204
14205/// Recover an outer terminator that tree-sitter assigned to a damaged nested
14206/// conditional. This occurs when a split construct such as `extern "C"`
14207/// consumes the nested `#endif` inside an error node: the nested conditional's
14208/// direct terminator is then the outer conditional's real terminator, while
14209/// the outer node ends with a missing token and absorbs later declarations.
14210fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14211    if !conditional.has_error()
14212        || conditional.child_by_field_name("alternative").is_some()
14213        || conditional
14214            .child(conditional.child_count().saturating_sub(1))
14215            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14216    {
14217        return None;
14218    }
14219    let mut recovered = None;
14220    for nested in named_children_iter(conditional) {
14221        if !matches!(
14222            nested.kind(),
14223            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14224        ) || nested.child_by_field_name("alternative").is_some()
14225        {
14226            continue;
14227        }
14228        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14229            continue;
14230        };
14231        if direct.kind() != "#endif" || direct.is_missing() {
14232            continue;
14233        }
14234        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14235            continue;
14236        };
14237        if displaced.end_byte() >= direct.start_byte() {
14238            continue;
14239        }
14240        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14241            recovered = Some(direct);
14242        }
14243    }
14244    recovered
14245}
14246
14247fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14248    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14249        return None;
14250    }
14251    let mut cursor = conditional.walk();
14252    let declarations = conditional
14253        .named_children(&mut cursor)
14254        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14255        .collect::<Vec<_>>();
14256    let declaration = *declarations.first()?;
14257    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14258        return None;
14259    }
14260    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14261    let mut terminator = None;
14262    let mut stack = (0..declaration.child_count())
14263        .filter_map(|index| declaration.child(index))
14264        .filter(|child| child.start_byte() < declarator_start)
14265        .map(|child| (child, false))
14266        .collect::<Vec<_>>();
14267    while let Some((node, inside_error)) = stack.pop() {
14268        let inside_error = inside_error || node.kind() == "ERROR";
14269        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14270            terminator = Some(node);
14271            continue;
14272        }
14273        for index in 0..node.child_count() {
14274            if let Some(child) = node.child(index)
14275                && child.start_byte() < declarator_start
14276            {
14277                stack.push((child, inside_error));
14278            }
14279        }
14280    }
14281    terminator
14282}
14283
14284fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14285    if !conditional.has_error()
14286        || conditional.child_by_field_name("alternative").is_some()
14287        || conditional
14288            .prev_named_sibling()
14289            .filter(|sibling| {
14290                sibling.kind() == "ERROR"
14291                    && sibling.child_count() == 1
14292                    && sibling
14293                        .child(0)
14294                        .is_some_and(|child| child.kind() == "typedef")
14295            })
14296            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14297            .is_none()
14298    {
14299        return None;
14300    }
14301    let mut cursor = conditional.walk();
14302    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14303    let declaration_index = children
14304        .iter()
14305        .position(|child| child.kind() == "declaration" && child.has_error())?;
14306    let declaration = children[declaration_index];
14307    if !children
14308        .iter()
14309        .skip(declaration_index + 1)
14310        .any(|child| child.end_byte() > declaration.end_byte())
14311    {
14312        return None;
14313    }
14314    let declarator = declaration.child_by_field_name("declarator")?;
14315    let mut error_end = None;
14316    let mut names = Vec::new();
14317    let mut stack = vec![declarator];
14318    while let Some(node) = stack.pop() {
14319        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14320            error_end =
14321                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14322            continue;
14323        }
14324        if matches!(node.kind(), "identifier" | "type_identifier") {
14325            names.push(node.start_byte());
14326        }
14327        push_named_children_reversed(node, &mut stack);
14328    }
14329    let error_end = error_end?;
14330    names
14331        .into_iter()
14332        .any(|start| start >= error_end)
14333        .then_some(declaration)
14334}
14335
14336fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14337    let Some(error) = parent.child(error_index) else {
14338        return false;
14339    };
14340    if error.kind() != "ERROR"
14341        || error.child_count() != 1
14342        || error.child(0).is_none_or(|child| child.kind() != "}")
14343    {
14344        return false;
14345    }
14346    let Some(semicolon) = parent.child(error_index + 1) else {
14347        return false;
14348    };
14349    semicolon.kind() == "expression_statement"
14350        && semicolon.child_count() == 1
14351        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14352}
14353
14354/// Locate the real end of a class-like declaration when a macro invocation
14355/// without a source semicolon absorbs the class's `};` into its parsed field.
14356/// The grammar then keeps following namespace declarations as later children
14357/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14358/// boundary structurally; no source-text delimiter scan is needed.
14359fn displaced_macro_class_tail(
14360    declaration_node: Node<'_>,
14361    body: Node<'_>,
14362    source: &str,
14363) -> Option<DisplacedMacroClassTail> {
14364    if !matches!(
14365        declaration_node.kind(),
14366        "class_specifier" | "struct_specifier" | "union_specifier"
14367    ) || body.kind() != "field_declaration_list"
14368    {
14369        return None;
14370    }
14371
14372    let child_count = body.named_child_count();
14373    for index in 0..child_count {
14374        let child = body.named_child(index)?;
14375        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14376            continue;
14377        };
14378        let split_index = index + 1;
14379        if split_index >= child_count {
14380            return None;
14381        }
14382        let mut cursor = body.walk();
14383        if !body
14384            .named_children(&mut cursor)
14385            .skip(split_index)
14386            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14387        {
14388            return None;
14389        }
14390        return Some(DisplacedMacroClassTail {
14391            split_index,
14392            class_range: Range {
14393                start_byte: declaration_node.start_byte(),
14394                end_byte: terminator.end_byte(),
14395                start_line: declaration_node.start_position().row + 1,
14396                end_line: terminator.end_position().row + 1,
14397            },
14398        });
14399    }
14400    None
14401}
14402
14403fn displaced_macro_field_terminator<'tree>(
14404    field: Node<'tree>,
14405    source: &str,
14406) -> Option<Node<'tree>> {
14407    if field.kind() != "field_declaration" {
14408        return None;
14409    }
14410    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14411    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14412    // grammar reads the whole invocation as a `macro_type_specifier`.
14413    let macro_type = field.child_by_field_name("type")?;
14414    let macro_name = match macro_type.kind() {
14415        "type_identifier"
14416            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14417        {
14418            macro_type
14419        }
14420        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14421        _ => return None,
14422    };
14423    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14424        return None;
14425    }
14426    for index in 0..field.child_count() {
14427        let error = field.child(index)?;
14428        if error.kind() != "ERROR"
14429            || error.child_count() != 1
14430            || error.child(0).is_none_or(|child| child.kind() != "}")
14431        {
14432            continue;
14433        }
14434        let semicolon = field.child(index + 1)?;
14435        if semicolon.kind() == ";" {
14436            return Some(semicolon);
14437        }
14438    }
14439    None
14440}
14441
14442fn recover_fragmented_partial_specialization<'tree>(
14443    template_node: Node<'tree>,
14444    declaration_child: Node<'tree>,
14445    source: &str,
14446    ancestry: &ParentIndex<'tree>,
14447) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14448    if declaration_child.kind() != "function_definition" {
14449        return None;
14450    }
14451    let class_node = declaration_child.child_by_field_name("type")?;
14452    if !matches!(
14453        class_node.kind(),
14454        "class_specifier" | "struct_specifier" | "union_specifier"
14455    ) || !class_node
14456        .child_by_field_name("name")
14457        .and_then(|name| direct_identifier_name(name, source))
14458        .is_some_and(|name| cpp_export_macro_token(&name))
14459    {
14460        return None;
14461    }
14462    let declarator = declaration_child.child_by_field_name("declarator")?;
14463    if declarator.kind() != "template_function" {
14464        return None;
14465    }
14466    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14467    if metadata.specialization_arguments.is_empty() {
14468        return None;
14469    }
14470    let body = declaration_child.child_by_field_name("body")?;
14471    if body.kind() != "compound_statement" {
14472        return None;
14473    }
14474    let complete_prefix = body.named_child(0).filter(|first| {
14475        first.kind() == "labeled_statement"
14476            && first.has_error()
14477            && first
14478                .named_child(first.named_child_count().saturating_sub(1))
14479                .is_some_and(recovered_declaration_has_class_terminator)
14480    });
14481    let complete_body = complete_prefix.is_some();
14482    let mut prefix_members = Vec::new();
14483    if let Some(prefix) = complete_prefix {
14484        prefix_members.push(prefix);
14485    } else {
14486        let mut body_cursor = body.walk();
14487        for child in body.named_children(&mut body_cursor) {
14488            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14489                break;
14490            }
14491            prefix_members.push(child);
14492        }
14493    }
14494    let containing_declarations = template_node.parent()?;
14495    if !matches!(
14496        containing_declarations.kind(),
14497        "declaration_list" | "compound_statement"
14498    ) {
14499        return None;
14500    }
14501    let mut member_siblings = Vec::new();
14502    let mut following_declarations = Vec::new();
14503    let terminator;
14504    if complete_body {
14505        terminator = complete_prefix?;
14506        let mut cursor = body.walk();
14507        let mut after_prefix = false;
14508        for child in body.named_children(&mut cursor) {
14509            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14510                after_prefix = true;
14511            } else if after_prefix {
14512                following_declarations.push(child);
14513            }
14514        }
14515    } else {
14516        let mut found_template = false;
14517        let mut cursor = containing_declarations.walk();
14518        let mut class_terminator = None;
14519        for child in containing_declarations.children(&mut cursor) {
14520            if same_node(child, template_node) {
14521                found_template = true;
14522                continue;
14523            }
14524            if found_template && child.kind() == "}" {
14525                class_terminator = Some(child);
14526                break;
14527            }
14528            // A namespace can never be a class member: reaching one before the
14529            // terminator proves the class's true close was swallowed upstream
14530            // and this scan has crossed into the enclosing scope, so the
14531            // recovery cannot be bounded -- continuing re-owns the namespace
14532            // block (and its template specializations) as class members under
14533            // a re-appended package, desyncing the fq boundary (#2306).
14534            if found_template && child.kind() == "namespace_definition" {
14535                return None;
14536            }
14537            if found_template && child.is_named() {
14538                member_siblings.push(child);
14539            }
14540        }
14541        terminator = class_terminator?;
14542    }
14543    let name = format!(
14544        "{}<{}>",
14545        metadata.primary_name,
14546        metadata
14547            .specialization_arguments
14548            .iter()
14549            .map(|argument| argument.text.as_str())
14550            .collect::<Vec<_>>()
14551            .join(", ")
14552    );
14553    Some(RecoveredFragmentedPartialSpecialization {
14554        declaration_node: declaration_child,
14555        name,
14556        range: Range {
14557            start_byte: declaration_child.start_byte(),
14558            end_byte: terminator.end_byte(),
14559            start_line: declaration_child.start_position().row + 1,
14560            end_line: terminator.end_position().row + 1,
14561        },
14562        prefix_members,
14563        member_siblings,
14564        following_declarations,
14565    })
14566}
14567
14568/// Whether `node` is the function-shaped parser wrapper whose body was
14569/// recovered as a fragmented partial-specialization class body.
14570///
14571/// Consumers that walk lexical function scopes must use the same complete
14572/// structural proof as declaration extraction. Otherwise member callables in
14573/// the recovered body are mistaken for function-local declarations.
14574pub fn is_recovered_fragmented_partial_specialization_container(
14575    node: Node<'_>,
14576    source: &str,
14577) -> bool {
14578    let Some(template) = node
14579        .parent()
14580        .filter(|parent| parent.kind() == "template_declaration")
14581    else {
14582        return false;
14583    };
14584    let mut root = template;
14585    while let Some(parent) = root.parent() {
14586        root = parent;
14587    }
14588    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14589        .is_some()
14590}
14591
14592fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14593    if declaration.kind() != "declaration" {
14594        return false;
14595    }
14596    // With an export macro between `class` and its name, tree-sitter folds a
14597    // complete class body into a function-shaped declaration. The class's own
14598    // `};` remains structurally identifiable as a direct ERROR child holding
14599    // `}`, immediately followed by the declaration's direct `;` child.
14600    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14601        let Some(error) = declaration.child(index) else {
14602            return false;
14603        };
14604        error.kind() == "ERROR"
14605            && error.child_count() == 1
14606            && error.child(0).is_some_and(|child| child.kind() == "}")
14607            && declaration
14608                .child(index + 1)
14609                .is_some_and(|child| child.kind() == ";")
14610    })
14611}
14612
14613fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14614    if node.has_error() {
14615        return false;
14616    }
14617    match node.kind() {
14618        "declaration"
14619        | "field_declaration"
14620        | "alias_declaration"
14621        | "type_definition"
14622        | "static_assert_declaration" => true,
14623        "labeled_statement" => node
14624            .named_child(node.named_child_count().saturating_sub(1))
14625            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14626        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14627            matches!(
14628                child.kind(),
14629                "declaration"
14630                    | "field_declaration"
14631                    | "alias_declaration"
14632                    | "type_definition"
14633                    | "function_definition"
14634            )
14635        }),
14636        _ => false,
14637    }
14638}
14639
14640fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14641    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14642        .then(|| node.child_by_field_name("declarator"))
14643        .flatten()
14644        .and_then(|declarator| extract_variable_name(declarator, source))
14645}
14646
14647use crate::graph::syntax::has_function_scope_ancestor;
14648
14649fn cpp_template_metadata<'tree>(
14650    template_node: Node<'tree>,
14651    declaration_child: Node<'tree>,
14652    source: &str,
14653    ancestry: &ParentIndex<'tree>,
14654) -> Option<CppTemplateMetadata> {
14655    let parameters_node = template_node.child_by_field_name("parameters")?;
14656    let name_node = cpp_templated_class_name_node(declaration_child)?;
14657    let primary_node = match name_node.kind() {
14658        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14659        _ => name_node,
14660    };
14661    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14662    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14663        return None;
14664    }
14665
14666    let mut parameter_nodes = Vec::new();
14667    let mut parameter_names = Vec::new();
14668    let mut cursor = parameters_node.walk();
14669    for parameter in parameters_node.named_children(&mut cursor) {
14670        if !matches!(
14671            parameter.kind(),
14672            "type_parameter_declaration"
14673                | "optional_type_parameter_declaration"
14674                | "variadic_type_parameter_declaration"
14675                | "template_template_parameter_declaration"
14676                | "parameter_declaration"
14677                | "optional_parameter_declaration"
14678                | "variadic_parameter_declaration"
14679        ) {
14680            continue;
14681        }
14682        let index = parameter_nodes.len();
14683        // An unnamed parameter still contributes template arity and kind. Use
14684        // an impossible C++ identifier so positional reconciliation can bind
14685        // it without making source expressions refer to a name that was not
14686        // written.
14687        let name = cpp_template_parameter_name(parameter, source)
14688            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14689        parameter_names.push(name);
14690        parameter_nodes.push(parameter);
14691    }
14692    let parameters = parameter_nodes
14693        .into_iter()
14694        .zip(parameter_names.iter().cloned())
14695        .map(|(parameter, name)| CppTemplateParameterMetadata {
14696            name,
14697            kind: cpp_template_parameter_kind(parameter),
14698            variadic: matches!(
14699                parameter.kind(),
14700                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14701            ),
14702            default: cpp_template_parameter_default_expression(
14703                parameter,
14704                source,
14705                &parameter_names,
14706                ancestry,
14707            ),
14708        })
14709        .collect();
14710    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14711        Vec::new()
14712    } else {
14713        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14714            .unwrap_or_default()
14715    };
14716    let alias_target = (declaration_child.kind() == "alias_declaration")
14717        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14718        .flatten();
14719    Some(CppTemplateMetadata {
14720        primary_name,
14721        primary_fq_name: String::new(),
14722        parameters,
14723        specialization_arguments,
14724        alias_target,
14725    })
14726}
14727
14728fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14729    match node.kind() {
14730        "class_specifier" | "struct_specifier" | "union_specifier" => {
14731            node.child_by_field_name("name")
14732        }
14733        "function_definition" => {
14734            let declarator = node.child_by_field_name("declarator")?;
14735            if matches!(declarator.kind(), "identifier" | "template_function") {
14736                Some(declarator)
14737            } else {
14738                None
14739            }
14740        }
14741        "alias_declaration" => node.child_by_field_name("name"),
14742        _ => None,
14743    }
14744}
14745
14746fn cpp_template_alias_target<'tree>(
14747    alias: Node<'tree>,
14748    source: &str,
14749    parameter_names: &[String],
14750    ancestry: &ParentIndex<'tree>,
14751) -> Option<CppTemplateAliasTargetMetadata> {
14752    let mut type_node = alias.child_by_field_name("type")?;
14753    while type_node.kind() == "type_descriptor" {
14754        type_node = type_node.child_by_field_name("type")?;
14755    }
14756    let global = type_node.child_by_field_name("scope").is_none()
14757        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14758    let mut components = Vec::new();
14759    cpp_template_target_components(type_node, source, &mut components)?;
14760    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14761    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14762        components,
14763        global,
14764        arguments,
14765    })
14766}
14767
14768fn cpp_template_target_components(
14769    node: Node<'_>,
14770    source: &str,
14771    out: &mut Vec<String>,
14772) -> Option<()> {
14773    // Delay reading the terminal name until the scope has been processed,
14774    // preserving the partial output even when a later component is missing.
14775    enum Component<'tree> {
14776        Node(Node<'tree>),
14777        Name(Node<'tree>),
14778    }
14779    let mut stack = vec![Component::Node(node)];
14780    while let Some(component) = stack.pop() {
14781        let node = match component {
14782            Component::Node(node) => node,
14783            Component::Name(node) => node.child_by_field_name("name")?,
14784        };
14785        match node.kind() {
14786            "identifier" | "namespace_identifier" | "type_identifier" => {
14787                out.push(node_text(node, source).to_string());
14788            }
14789            "template_type" => stack.push(Component::Name(node)),
14790            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14791                stack.push(Component::Name(node));
14792                if let Some(scope) = node.child_by_field_name("scope") {
14793                    stack.push(Component::Node(scope));
14794                }
14795            }
14796            _ => return None,
14797        }
14798    }
14799    Some(())
14800}
14801
14802fn cpp_template_argument_expressions<'tree>(
14803    mut node: Node<'tree>,
14804    source: &str,
14805    parameter_names: &[String],
14806    ancestry: &ParentIndex<'tree>,
14807) -> Option<Vec<CppTemplateExpression>> {
14808    loop {
14809        match node.kind() {
14810            "template_type" | "template_function" => {
14811                let arguments = node.child_by_field_name("arguments")?;
14812                let mut cursor = arguments.walk();
14813                return Some(
14814                    arguments
14815                        .named_children(&mut cursor)
14816                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14817                        .map(|argument| {
14818                            cpp_template_expression(argument, source, parameter_names, ancestry)
14819                        })
14820                        .collect(),
14821                );
14822            }
14823            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14824                node = node
14825                    .child_by_field_name("name")
14826                    .or_else(|| node.child_by_field_name("type"))?;
14827            }
14828            _ => return None,
14829        }
14830    }
14831}
14832
14833fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14834    if node.kind() == "template_template_parameter_declaration" {
14835        let mut cursor = node.walk();
14836        return node
14837            .named_children(&mut cursor)
14838            .find(|child| {
14839                matches!(
14840                    child.kind(),
14841                    "type_parameter_declaration"
14842                        | "optional_type_parameter_declaration"
14843                        | "variadic_type_parameter_declaration"
14844                )
14845            })
14846            .expect("a template-template parameter has a type parameter declaration");
14847    }
14848    node
14849}
14850
14851pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14852    let node = cpp_template_parameter_declaration(node);
14853    let candidate = node
14854        .child_by_field_name("name")
14855        .or_else(|| node.child_by_field_name("declarator"))
14856        .or_else(|| cpp_recovered_type_parameter_name(node))
14857        .or_else(|| {
14858            let mut cursor = node.walk();
14859            node.named_children(&mut cursor).find(|child| {
14860                matches!(
14861                    child.kind(),
14862                    "identifier" | "type_identifier" | "field_identifier"
14863                )
14864            })
14865        })?;
14866    if let Some(name) = direct_identifier_name(candidate, source) {
14867        return Some(name);
14868    }
14869    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
14870    (!name.is_empty()).then_some(name)
14871}
14872
14873pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
14874    match node.kind() {
14875        "type_parameter_declaration"
14876        | "optional_type_parameter_declaration"
14877        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
14878        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
14879        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
14880        _ => CppTemplateParameterKind::Value,
14881    }
14882}
14883
14884/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
14885/// non-type parameter. The grammar's `default_type` cannot hold an abstract
14886/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
14887/// sibling; since grammar 0.24.4 the parameter itself also becomes an
14888/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
14889/// no declarator and `default_value: T`. A valid dependent type names a
14890/// qualified type after `typename`, so a bare type identifier there is the
14891/// type parameter's name.
14892pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
14893    if parameter.kind() != "optional_parameter_declaration"
14894        || parameter.child_by_field_name("declarator").is_some()
14895    {
14896        return None;
14897    }
14898    let dependent = parameter
14899        .child_by_field_name("type")
14900        .filter(|type_node| type_node.kind() == "dependent_type")?;
14901    let mut cursor = dependent.walk();
14902    let mut named = dependent
14903        .named_children(&mut cursor)
14904        .filter(|child| !child.is_extra());
14905    match (named.next(), named.next()) {
14906        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
14907        _ => None,
14908    }
14909}
14910
14911#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
14912pub struct CppTemplateParameterNameMatches {
14913    pub any_parameter: bool,
14914    pub type_parameter: bool,
14915}
14916
14917pub fn cpp_template_parameter_name_matches(
14918    parameters: Node<'_>,
14919    name: &str,
14920    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
14921) -> CppTemplateParameterNameMatches {
14922    let mut matches = CppTemplateParameterNameMatches::default();
14923    let mut cursor = parameters.walk();
14924    for parameter in parameters.named_children(&mut cursor) {
14925        if parameter_name(parameter).as_deref() == Some(name) {
14926            matches.any_parameter = true;
14927            matches.type_parameter |=
14928                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
14929        }
14930    }
14931    matches
14932}
14933
14934pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
14935    parent.child_by_field_name("type").is_some_and(|type_node| {
14936        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
14937    })
14938}
14939
14940fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
14941    let node = cpp_template_parameter_declaration(node);
14942    node.child_by_field_name("default_type")
14943        .or_else(|| node.child_by_field_name("default_value"))
14944}
14945
14946fn cpp_template_parameter_default_expression<'tree>(
14947    parameter: Node<'tree>,
14948    source: &str,
14949    parameter_names: &[String],
14950    ancestry: &ParentIndex<'tree>,
14951) -> Option<CppTemplateExpression> {
14952    let default = cpp_template_parameter_default(parameter)?;
14953    let base = cpp_template_expression(default, source, parameter_names, ancestry);
14954    let Some(pointer_error) = parameter.next_named_sibling() else {
14955        return Some(base);
14956    };
14957    // The grammar's default_type accepts a type specifier, not a complete
14958    // type-id. For `const T*` it calls `const` the type and leaves `T*` in
14959    // the next ERROR. Let the type-id grammar interpret that exact range.
14960    if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
14961        && pointer_error.is_error()
14962    {
14963        let text = &source[default.start_byte()..pointer_error.end_byte()];
14964        let parsed = crate::graph::syntax::parse_sentinel(
14965            "using __bifrost_default = ",
14966            crate::graph::syntax::SentinelInput::region(text),
14967            ";",
14968        );
14969        let root = parsed.tree.root_node();
14970        if !root.has_error()
14971            && let Some(type_node) =
14972                brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
14973                    root,
14974                    |child| !child.is_extra(),
14975                )
14976                .filter(|alias| alias.kind() == "alias_declaration")
14977                .and_then(|alias| alias.child_by_field_name("type"))
14978        {
14979            let alias_ancestry = ParentIndex::new(root);
14980            return Some(cpp_template_expression(
14981                type_node,
14982                &parsed.source,
14983                parameter_names,
14984                &alias_ancestry,
14985            ));
14986        }
14987    }
14988    let Some(pointer_declarator) =
14989        recovered_abstract_pointer_declarator_term(pointer_error, source)
14990    else {
14991        return Some(base);
14992    };
14993    Some(CppTemplateExpression {
14994        text: format!(
14995            "{}{}",
14996            base.text,
14997            normalize_cpp_whitespace(node_text(pointer_error, source))
14998        ),
14999        term: CppTemplateTerm::Node {
15000            kind: "type_descriptor".to_string(),
15001            children: vec![base.term, pointer_declarator],
15002        },
15003    })
15004}
15005
15006fn recovered_abstract_pointer_declarator_term(
15007    node: Node<'_>,
15008    source: &str,
15009) -> Option<CppTemplateTerm> {
15010    if node.kind() != "ERROR" || node.child_count() == 0 {
15011        return None;
15012    }
15013    let mut children = Vec::new();
15014    for index in 0..node.child_count() {
15015        let child = node.child(index)?;
15016        if child.kind() != "*" {
15017            return None;
15018        }
15019        children.push(CppTemplateTerm::Atom {
15020            kind: "*".to_string(),
15021            text: normalize_cpp_whitespace(node_text(child, source)),
15022        });
15023    }
15024    Some(CppTemplateTerm::Node {
15025        kind: "abstract_pointer_declarator".to_string(),
15026        children,
15027    })
15028}
15029
15030fn cpp_template_expression<'tree>(
15031    node: Node<'tree>,
15032    source: &str,
15033    parameter_names: &[String],
15034    ancestry: &ParentIndex<'tree>,
15035) -> CppTemplateExpression {
15036    let text = normalize_cpp_whitespace(node_text(node, source));
15037    CppTemplateExpression {
15038        text,
15039        term: cpp_template_term(node, source, parameter_names, ancestry),
15040    }
15041}
15042
15043pub fn cpp_template_term<'tree>(
15044    node: Node<'tree>,
15045    source: &str,
15046    parameter_names: &[String],
15047    ancestry: &ParentIndex<'tree>,
15048) -> CppTemplateTerm {
15049    enum Work<'tree> {
15050        Visit(Node<'tree>),
15051        Build { kind: String, child_count: usize },
15052    }
15053
15054    let mut work = vec![Work::Visit(node)];
15055    let mut terms = Vec::new();
15056    while let Some(next) = work.pop() {
15057        match next {
15058            Work::Visit(current) => {
15059                let text = normalize_cpp_whitespace(node_text(current, source));
15060                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15061                    terms.push(CppTemplateTerm::Parameter(text));
15062                    continue;
15063                }
15064                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15065                    let mut cursor = current.walk();
15066                    let named = current
15067                        .named_children(&mut cursor)
15068                        .filter(|child| !child.is_extra() && child.kind() != "comment")
15069                        .collect::<Vec<_>>();
15070                    if let [child] = named.as_slice() {
15071                        work.push(Work::Visit(*child));
15072                        continue;
15073                    }
15074                }
15075                if current.child_count() == 0 {
15076                    terms.push(CppTemplateTerm::Atom {
15077                        kind: if matches!(
15078                            current.kind(),
15079                            "identifier"
15080                                | "type_identifier"
15081                                | "field_identifier"
15082                                | "namespace_identifier"
15083                        ) {
15084                            "identifier".to_string()
15085                        } else {
15086                            current.kind().to_string()
15087                        },
15088                        text,
15089                    });
15090                    continue;
15091                }
15092                let children = (0..current.child_count())
15093                    .filter_map(|index| current.child(index))
15094                    .filter(|child| !child.is_extra() && child.kind() != "comment")
15095                    .collect::<Vec<_>>();
15096                work.push(Work::Build {
15097                    kind: current.kind().to_string(),
15098                    child_count: children.len(),
15099                });
15100                work.extend(children.into_iter().rev().map(Work::Visit));
15101            }
15102            Work::Build { kind, child_count } => {
15103                let children = terms.split_off(terms.len() - child_count);
15104                terms.push(CppTemplateTerm::Node { kind, children });
15105            }
15106        }
15107    }
15108    terms.pop().expect("template term traversal emits one root")
15109}
15110
15111fn cpp_template_term_leaf_is_parameter<'tree>(
15112    node: Node<'tree>,
15113    text: &str,
15114    parameter_names: &[String],
15115    ancestry: &ParentIndex<'tree>,
15116) -> bool {
15117    if !parameter_names.iter().any(|parameter| parameter == text) {
15118        return false;
15119    }
15120    !ancestry.parent(node).is_some_and(|parent| {
15121        matches!(
15122            parent.kind(),
15123            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15124        ) && parent.child_by_field_name("scope").is_some()
15125            && parent.child_by_field_name("name") == Some(node)
15126    })
15127}
15128
15129fn enclosing_cpp_declaration_node<'tree>(
15130    mut node: Node<'tree>,
15131    ancestry: &ParentIndex<'tree>,
15132) -> Option<Node<'tree>> {
15133    loop {
15134        match node.kind() {
15135            "declaration"
15136            | "function_declaration"
15137            | "field_declaration"
15138            | "function_definition" => return Some(node),
15139            _ => node = ancestry.parent(node)?,
15140        }
15141    }
15142}
15143
15144fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15145    let mut params = Vec::new();
15146    let mut cursor = parameters_node.walk();
15147    for child in parameters_node.children(&mut cursor) {
15148        match child.kind() {
15149            "explicit_object_parameter_declaration" => {
15150                if let Some(parameter) = child
15151                    .named_children(&mut child.walk())
15152                    .find(|node| node.kind() == "parameter_declaration")
15153                {
15154                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15155                }
15156            }
15157            "parameter_declaration" | "optional_parameter_declaration" => {
15158                params.push(cpp_parameter_type(child, source));
15159            }
15160            "variadic_parameter_declaration" => {
15161                params.push(cpp_parameter_type(child, source));
15162            }
15163            "variadic_parameter" | "..." => params.push("...".to_string()),
15164            _ => {}
15165        }
15166    }
15167
15168    if params.is_empty() {
15169        "()".to_string()
15170    } else {
15171        format!("({})", params.join(", "))
15172    }
15173}
15174
15175/// Where a C or C++ callable declaration is written, which is what decides
15176/// which modifier facts its own node is able to state.
15177enum CppCallableScope<'tree> {
15178    /// Written directly in a class, struct or union body. The declaration
15179    /// states its own storage class, and its access is the one the body's
15180    /// specifier ladder is under at that point.
15181    ClassBody {
15182        body: Node<'tree>,
15183        default_visibility: DeclaredVisibility,
15184    },
15185    /// A qualified declaration written outside every class body
15186    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
15187    /// writes no access specifier, and whether the qualifier names a class or
15188    /// a namespace is not decidable from this file alone -- that is exactly
15189    /// what [`crate::reconcile`] needs the include-visible class table for.
15190    Qualified,
15191    /// Namespace scope or file scope, reached by an unqualified name. C has no
15192    /// member functions at all, so every C callable lands here.
15193    Unqualified,
15194}
15195
15196/// Where the declaration sits, from one upward walk of its own ancestry.
15197///
15198/// The walk stops as soon as something proves the declaration is outside every
15199/// class body: a `namespace_definition`, because a class body never encloses
15200/// one, and a `compound_statement`, because a body opens a block scope and
15201/// nothing declared in it is a member of the class the enclosing definition
15202/// belongs to. That is the same rule
15203/// `CppVisitor::visit_local_types_in_function` applies when it
15204/// drops the aggregate carrier, and it keeps this question off the O(depth)
15205/// walk a deeply nested generated header would otherwise pay per callable.
15206///
15207/// `function_declarator` is `None` for a declaration whose callable the macro
15208/// recovery reparsed out of a malformed member: there is no declarator node to
15209/// ask whether the name carries a qualifier, and a recovered member is written
15210/// in its class body anyway.
15211fn cpp_callable_scope<'tree>(
15212    declaration: Node<'tree>,
15213    function_declarator: Option<Node<'tree>>,
15214    ancestry: &ParentIndex<'tree>,
15215) -> CppCallableScope<'tree> {
15216    let mut current = Some(declaration);
15217    while let Some(node) = current {
15218        match node.kind() {
15219            "field_declaration_list" => {
15220                let default_visibility =
15221                    ancestry
15222                        .parent(node)
15223                        .map_or(DeclaredVisibility::Unknown, |class_like| {
15224                            // `struct` and `union` members default to public;
15225                            // `class` members default to private.
15226                            match class_like.kind() {
15227                                "struct_specifier" | "union_specifier" => {
15228                                    DeclaredVisibility::Public
15229                                }
15230                                _ => DeclaredVisibility::Private,
15231                            }
15232                        });
15233                return CppCallableScope::ClassBody {
15234                    body: node,
15235                    default_visibility,
15236                };
15237            }
15238            "namespace_definition" | "compound_statement" | "translation_unit" => break,
15239            _ => {}
15240        }
15241        current = ancestry.parent(node);
15242    }
15243    if cpp_callable_declarator_is_qualified(function_declarator) {
15244        CppCallableScope::Qualified
15245    } else {
15246        CppCallableScope::Unqualified
15247    }
15248}
15249
15250/// Whether the declarator's own written name carries a `::` qualifier.
15251///
15252/// A qualified declaration written outside every class body
15253/// (`long Client::send(int) {}`) names a member whose class body this file may
15254/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
15255/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
15256fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15257    function_declarator
15258        .and_then(cpp_function_declarator_name_node)
15259        .is_some_and(|name| name.kind() == "qualified_identifier")
15260}
15261
15262/// The access one class-body member declaration is written under.
15263///
15264/// C++ states member access positionally: an `access_specifier` applies to
15265/// every member written after it until the next one, and the members before
15266/// the first one take the class keyword's default. The answer is therefore the
15267/// last specifier the body writes before the member's own list-level node,
15268/// read from the specifier's own keyword token rather than from its text. A
15269/// label a macro invocation swallowed has no specifier node or keyword token;
15270/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
15271/// grammar made of the keyword.
15272///
15273/// The body's children are read forward with one cursor rather than backwards
15274/// from the member: `Node::prev_sibling` re-descends from the root for each
15275/// step, which is the per-edge cost #2361 keeps off this walk.
15276fn cpp_member_declared_visibility<'tree>(
15277    declaration: Node<'tree>,
15278    body: Node<'tree>,
15279    default_visibility: DeclaredVisibility,
15280    source: &str,
15281    ancestry: &ParentIndex<'tree>,
15282) -> DeclaredVisibility {
15283    let mut list_level = declaration;
15284    while ancestry.parent(list_level) != Some(body) {
15285        let Some(parent) = ancestry.parent(list_level) else {
15286            return default_visibility;
15287        };
15288        list_level = parent;
15289    }
15290    let mut visibility = default_visibility;
15291    let mut cursor = body.walk();
15292    for child in body.children(&mut cursor) {
15293        if child == list_level {
15294            break;
15295        }
15296        let access = if child.kind() == "access_specifier" {
15297            child.child(0).map(|keyword| keyword.kind())
15298        } else {
15299            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15300        };
15301        visibility = match access {
15302            Some("public") => DeclaredVisibility::Public,
15303            Some("protected") => DeclaredVisibility::Protected,
15304            Some("private") => DeclaredVisibility::Private,
15305            _ => visibility,
15306        };
15307    }
15308    visibility
15309}
15310
15311/// Whether this declaration writes the `static` storage class specifier.
15312///
15313/// What `static` *means* is the scope's business: on a class-body member it is
15314/// the receiver fact, and at file scope it is internal linkage, which is why
15315/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15316/// question and answer it differently.
15317fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15318    let mut cursor = declaration.walk();
15319    declaration.named_children(&mut cursor).any(|child| {
15320        child.kind() == "storage_class_specifier"
15321            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15322    })
15323}
15324
15325/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15326/// publishes, as C and C++ state them.
15327struct CppCallableModifiers {
15328    is_static: bool,
15329    is_constructor: bool,
15330    visibility: DeclaredVisibility,
15331}
15332
15333/// The modifier facts a C or C++ callable declaration states in its own shape.
15334///
15335/// C++ has no `static` keyword for receivers and no visibility keyword of the
15336/// Java kind: it states both in the declaration's position. A member declared
15337/// in a class body binds the object it is selected on unless that declaration
15338/// writes the `static` storage class, and its access is the one the body's
15339/// specifier ladder is under. A function at namespace or file scope binds
15340/// nothing and has no access specifier at all, so its declared visibility is
15341/// Unknown. Linkage is recorded separately and does not declare access. C has
15342/// no member functions, and the `static` a C function writes is internal linkage,
15343/// never a receiver fact.
15344///
15345/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15346/// declaration. Without them it reported no contract at all,
15347/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15348/// callable, no reviewed summary could name one, and every effect or taint walk
15349/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15350/// in #3455, Kotlin in #3453, Python in #3451.
15351///
15352/// An out-of-line member definition cannot repeat `static` or access. These
15353/// are per-file facts only: the analyzer reconciles the exact overload with
15354/// include-visible class declarations before exposing its receiver contract
15355/// (#3509). A missing declaration leaves that contract unknown.
15356fn cpp_callable_modifiers<'tree>(
15357    declaration: Node<'tree>,
15358    function_declarator: Option<Node<'tree>>,
15359    is_constructor: bool,
15360    source: &str,
15361    ancestry: &ParentIndex<'tree>,
15362) -> CppCallableModifiers {
15363    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15364        CppCallableScope::ClassBody {
15365            body,
15366            default_visibility,
15367        } => CppCallableModifiers {
15368            is_static: cpp_declares_static_storage_class(declaration, source),
15369            is_constructor,
15370            visibility: cpp_member_declared_visibility(
15371                declaration,
15372                body,
15373                default_visibility,
15374                source,
15375                ancestry,
15376            ),
15377        },
15378        CppCallableScope::Qualified => CppCallableModifiers {
15379            is_static: false,
15380            is_constructor,
15381            visibility: DeclaredVisibility::Unknown,
15382        },
15383        CppCallableScope::Unqualified => CppCallableModifiers {
15384            is_static: false,
15385            is_constructor,
15386            visibility: DeclaredVisibility::Unknown,
15387        },
15388    }
15389}
15390
15391/// The persisted signature facts one C or C++ callable declaration publishes.
15392///
15393/// `linkage` is the caller's, not this function's: the node that carries the
15394/// storage class is not always the declaration this declarator sits in -- a
15395/// recovered constructor reads it from its class declaration, and a
15396/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15397/// visit site that knows which node states it passes the answer in. Recording
15398/// it here keeps it beside the declared visibility it also decides.
15399fn cpp_signature_metadata<'tree>(
15400    signature: String,
15401    function_declarator: Node<'tree>,
15402    linkage: CallableLinkage,
15403    source: &str,
15404    ancestry: &ParentIndex<'tree>,
15405) -> SignatureMetadata {
15406    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15407    let is_constructor =
15408        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15409    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15410        Some(declaration) => cpp_callable_modifiers(
15411            declaration,
15412            Some(function_declarator),
15413            is_constructor,
15414            source,
15415            ancestry,
15416        ),
15417        // Severe recovery left no declaration node above this declarator, so
15418        // nothing is stated here beyond what the callable's own name proves.
15419        None => CppCallableModifiers {
15420            is_static: false,
15421            is_constructor,
15422            visibility: DeclaredVisibility::Unknown,
15423        },
15424    };
15425    let enrich = |mut metadata: SignatureMetadata| {
15426        metadata = metadata.with_overload_discriminator(Some(cpp_overload_discriminator(
15427            function_declarator,
15428            source,
15429        )));
15430        // #3507: a qualified definition repeats none of the class body's
15431        // dispatch facts, so this row publishes none either.
15432        if let Some(dispatch) = dispatch {
15433            metadata = metadata.with_dispatch_extensibility(dispatch);
15434        }
15435        metadata
15436            .with_callable_linkage(linkage)
15437            .with_callable_modifiers(
15438                modifiers.is_static,
15439                modifiers.is_constructor,
15440                modifiers.visibility,
15441            )
15442    };
15443    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15444    let return_type_identity =
15445        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15446    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15447        return enrich(
15448            SignatureMetadata::new(signature, Vec::new())
15449                .with_return_type_text(return_type_text)
15450                .with_return_type_identity(return_type_identity),
15451        );
15452    };
15453    let callable_arity = cpp_callable_arity(parameters_node, source);
15454    let callable_parameter_types = cpp_callable_parameter_types(parameters_node, source);
15455    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15456    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15457    let Some(relative_start) = signature
15458        .get(search_from..)
15459        .and_then(|suffix| suffix.find(&parameter_text))
15460    else {
15461        return enrich(
15462            SignatureMetadata::new(signature, Vec::new())
15463                .with_callable_arity(callable_arity)
15464                .with_callable_parameter_types(callable_parameter_types)
15465                .with_return_type_text(return_type_text)
15466                .with_return_type_identity(return_type_identity),
15467        );
15468    };
15469    let parameters_start = search_from + relative_start;
15470    let parameters_end = parameters_start + parameter_text.len();
15471    let mut search_start = parameters_start;
15472    let parameters = cpp_parameter_label_nodes(parameters_node)
15473        .into_iter()
15474        .filter_map(|label_node| {
15475            let label = normalize_cpp_whitespace(node_text(label_node, source));
15476            if label.is_empty() || search_start > parameters_end {
15477                return None;
15478            }
15479            let haystack = signature.get(search_start..parameters_end)?;
15480            let relative_start = haystack.find(&label)?;
15481            let start_byte = search_start + relative_start;
15482            let end_byte = start_byte + label.len();
15483            search_start = end_byte;
15484            Some(ParameterMetadata::new(label, start_byte, end_byte))
15485        })
15486        .collect();
15487    enrich(
15488        SignatureMetadata::new(signature, parameters)
15489            .with_callable_arity(callable_arity)
15490            .with_callable_parameter_types(callable_parameter_types)
15491            .with_return_type_text(return_type_text)
15492            .with_return_type_identity(return_type_identity),
15493    )
15494}
15495
15496pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15497    function_declarator: Node<'tree>,
15498    source: &str,
15499    ancestry: &ParentIndex<'tree>,
15500) -> bool {
15501    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15502        .or_else(|| function_declarator.child_by_field_name("name"))
15503        .or_else(|| last_named_child(function_declarator))
15504    else {
15505        return false;
15506    };
15507    // An out-of-line definition writes the class in the name itself
15508    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15509    // terminal component answers without any enclosing class body.
15510    if name_node.kind() == "qualified_identifier" {
15511        let owner = name_node
15512            .child_by_field_name("scope")
15513            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15514        let member = name_node
15515            .child_by_field_name("name")
15516            .and_then(|name| canonical_cpp_qualified_component(name, source));
15517        return match (owner, member) {
15518            (Some(owner), Some(member)) => owner.name == member.name,
15519            _ => false,
15520        };
15521    }
15522    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15523        return false;
15524    };
15525
15526    let mut current = ancestry.parent(function_declarator);
15527    while let Some(ancestor) = current {
15528        let owner_name = match ancestor.kind() {
15529            "class_specifier" | "struct_specifier" | "union_specifier" => {
15530                class_like_name(ancestor, source, ancestry)
15531            }
15532            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15533            _ => None,
15534        };
15535        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15536            return true;
15537        }
15538        current = ancestry.parent(ancestor);
15539    }
15540    false
15541}
15542
15543/// Recover the owner name from the direct grammar shape retained when a later
15544/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15545/// `ERROR` node:
15546///
15547/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15548///
15549/// Direct-child checks keep this distinct from an unrelated nested class inside
15550/// a broader error region. The closing brace may be displaced past the error
15551/// node, so the opening body token is the available structural boundary.
15552fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15553    if node.kind() != "ERROR" {
15554        return None;
15555    }
15556    let keyword = node.child(0)?;
15557    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15558        return None;
15559    }
15560    let name_node = node.child(1)?;
15561    let name = direct_identifier_name(name_node, source)?;
15562    let has_body = (2..node.child_count())
15563        .filter_map(|index| node.child(index))
15564        .any(|child| child.kind() == "{");
15565    has_body.then_some(name)
15566}
15567
15568/// Whether one declaration carries a native must-use attribute.
15569///
15570/// Attribute names are read only from structurally selected attribute nodes;
15571/// an identifier with the same spelling elsewhere in the declaration cannot
15572/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15573/// while standard `nodiscard` is accepted without a namespace prefix.
15574pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15575    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15576    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15577    // that one structural sibling into the same declaration boundary; this
15578    // does not widen the walk to surrounding declarations.
15579    let declaration = declaration
15580        .parent()
15581        .filter(|parent| {
15582            parent.kind() == "declaration"
15583                && parent
15584                    .child_by_field_name("type")
15585                    .is_some_and(|type_node| type_node.id() == declaration.id())
15586        })
15587        .unwrap_or(declaration);
15588    let mut pending = vec![declaration];
15589    while let Some(node) = pending.pop() {
15590        if cpp_attribute_node_is_native_must_use(node, source)
15591            && cpp_attribute_is_attached_to_declaration(node, declaration)
15592        {
15593            return true;
15594        }
15595        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15596        {
15597            continue;
15598        }
15599        push_named_children_reversed(node, &mut pending);
15600    }
15601    false
15602}
15603
15604fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15605    if node.kind() == "attribute" {
15606        return node.child_by_field_name("name").is_some_and(|name| {
15607            let name = node_text(name, source);
15608            let prefix = node
15609                .child_by_field_name("prefix")
15610                .map(|prefix| node_text(prefix, source));
15611            matches!(
15612                (prefix, name),
15613                (None, "nodiscard")
15614                    | (None, "warn_unused_result")
15615                    | (Some("gnu"), "warn_unused_result")
15616            )
15617        });
15618    }
15619    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15620    {
15621        return false;
15622    }
15623    let Some(arguments) = node.named_child(0) else {
15624        return false;
15625    };
15626    arguments.kind() == "argument_list"
15627        && arguments.named_children(&mut arguments.walk()).any(|name| {
15628            name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15629        })
15630}
15631
15632fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15633    let mut current = attribute;
15634    while let Some(parent) = current.parent() {
15635        if parent.id() == declaration.id() {
15636            return true;
15637        }
15638        if matches!(
15639            parent.kind(),
15640            "declaration"
15641                | "field_declaration"
15642                | "function_definition"
15643                | "struct_specifier"
15644                | "class_specifier"
15645                | "union_specifier"
15646                | "enum_specifier"
15647        ) {
15648            return false;
15649        }
15650        current = parent;
15651    }
15652    false
15653}
15654
15655/// Whether export/decorator macros ahead of a callable declaration's `type`
15656/// make that type head decoration rather than a semantic return type. The
15657/// older grammar leaves the macro token in the `type` field and the real type
15658/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15659/// leading `macro_modifier`, or the whole invocation as a
15660/// `macro_type_specifier` type. Such a declaration does not carry enough
15661/// structured evidence to persist a return type, as text or as identity.
15662fn cpp_declaration_return_type_is_macro_decorated(
15663    declaration: Node<'_>,
15664    type_node: Node<'_>,
15665    source: &str,
15666) -> bool {
15667    type_node.kind() == "macro_type_specifier"
15668        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15669        || (cpp_export_macro_token(node_text(type_node, source))
15670            && (0..declaration.named_child_count()).any(|index| {
15671                declaration
15672                    .named_child(index)
15673                    .is_some_and(|child| child.kind() == "ERROR")
15674            }))
15675}
15676
15677/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15678/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15679/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15680pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15681    declaration: Node<'_>,
15682    type_node: Node<'_>,
15683) -> bool {
15684    let mut head_cursor = declaration.walk();
15685    declaration
15686        .named_children(&mut head_cursor)
15687        .take_while(|child| *child != type_node)
15688        .any(|child| child.kind() == "macro_modifier")
15689}
15690
15691fn cpp_callable_return_type_parts<'tree>(
15692    function_declarator: Node<'tree>,
15693    source: &str,
15694    ancestry: &ParentIndex<'tree>,
15695) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15696    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15697        return None;
15698    }
15699    if let Some((return_type, _)) =
15700        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15701    {
15702        return Some((return_type, Vec::new()));
15703    }
15704    let mut cursor = function_declarator.walk();
15705    if let Some(trailing) = function_declarator
15706        .named_children(&mut cursor)
15707        .find(|child| child.kind() == "trailing_return_type")
15708        && let Some(type_descriptor) = trailing.named_child(0)
15709    {
15710        return Some((type_descriptor, Vec::new()));
15711    }
15712
15713    let mut current = function_declarator;
15714    let mut wrappers = Vec::new();
15715    while let Some(parent) = ancestry.parent(current) {
15716        if matches!(
15717            parent.kind(),
15718            "function_definition" | "declaration" | "field_declaration"
15719        ) {
15720            let type_node = parent.child_by_field_name("type")?;
15721            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15722                return None;
15723            }
15724            return Some((type_node, wrappers));
15725        }
15726        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15727            || (matches!(
15728                parent.kind(),
15729                "pointer_declarator"
15730                    | "reference_declarator"
15731                    | "array_declarator"
15732                    | "parenthesized_declarator"
15733            ) && parent.named_child_count() == 1
15734                && parent.named_child(0) == Some(current));
15735        if !wraps_current_declarator {
15736            return None;
15737        }
15738        match parent.kind() {
15739            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15740            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15741            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15742            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15743            _ => return None,
15744        }
15745        current = parent;
15746    }
15747    None
15748}
15749
15750/// Return the AST node that describes one callable's declared result type.
15751///
15752/// The node is returned only after the same declarator walk that establishes
15753/// the persisted C++ return identity. Consumers can therefore resolve a
15754/// nominal result or inspect its declaration attributes without parsing a
15755/// rendered signature.
15756pub fn cpp_callable_declaration_return_type_node<'tree>(
15757    callable: Node<'tree>,
15758    source: &str,
15759    ancestry: &ParentIndex<'tree>,
15760) -> Option<Node<'tree>> {
15761    let declarator = callable
15762        .child_by_field_name("declarator")
15763        .and_then(extract_function_declarator)?;
15764    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15765}
15766
15767/// Return the structured declared result type of one callable declaration.
15768///
15769/// This accepts the enclosing declaration node so consumers do not need to
15770/// duplicate the declarator-unwrapping rules before asking for the structured
15771/// identity.
15772pub fn cpp_callable_declaration_return_type_identity<'tree>(
15773    callable: Node<'tree>,
15774    source: &str,
15775    ancestry: &ParentIndex<'tree>,
15776) -> Option<StructuredTypeIdentity> {
15777    let declarator = callable
15778        .child_by_field_name("declarator")
15779        .and_then(extract_function_declarator)?;
15780    cpp_callable_return_type_identity(declarator, source, ancestry)
15781}
15782
15783pub(crate) fn cpp_callable_return_type_identity<'tree>(
15784    function_declarator: Node<'tree>,
15785    source: &str,
15786    ancestry: &ParentIndex<'tree>,
15787) -> Option<StructuredTypeIdentity> {
15788    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15789    let (return_type, wrappers) =
15790        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15791    cpp_apply_structured_type_wrappers(
15792        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15793        wrappers,
15794    )
15795}
15796
15797fn cpp_structured_type_identity(
15798    node: Node<'_>,
15799    source: &str,
15800    lexical_scope: &[String],
15801) -> Option<StructuredTypeIdentity> {
15802    enum Work<'tree> {
15803        Visit(Node<'tree>),
15804        Wrap(CppStructuredTypeWrapper),
15805        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15806        BuildGeneric { argument_count: usize },
15807    }
15808
15809    let mut work = vec![Work::Visit(node)];
15810    let mut values = Vec::new();
15811    let mut builder = StructuredTypeIdentityBuilder::default();
15812    while let Some(next) = work.pop() {
15813        match next {
15814            Work::Visit(current) => match current.kind() {
15815                "type_descriptor" => {
15816                    let type_node = current
15817                        .child_by_field_name("type")
15818                        .or_else(|| current.named_child(0))?;
15819                    let mut wrappers = Vec::new();
15820                    let mut cursor = current.walk();
15821                    for child in current.named_children(&mut cursor) {
15822                        if child.id() != type_node.id() {
15823                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15824                        }
15825                    }
15826                    work.push(Work::ApplyWrappers(wrappers));
15827                    work.push(Work::Visit(type_node));
15828                }
15829                "pointer_declarator" | "abstract_pointer_declarator" => {
15830                    let child = current
15831                        .child_by_field_name("declarator")
15832                        .or_else(|| current.named_child(0))?;
15833                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15834                    work.push(Work::Visit(child));
15835                }
15836                "reference_declarator" => {
15837                    let child = current
15838                        .child_by_field_name("declarator")
15839                        .or_else(|| current.named_child(0))?;
15840                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15841                    work.push(Work::Visit(child));
15842                }
15843                "array_declarator" | "abstract_array_declarator" => {
15844                    let child = current
15845                        .child_by_field_name("declarator")
15846                        .or_else(|| current.named_child(0))?;
15847                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15848                    work.push(Work::Visit(child));
15849                }
15850                "template_type" => {
15851                    let name_node = current.child_by_field_name("name")?;
15852                    let arguments = current
15853                        .child_by_field_name("arguments")
15854                        .map(|arguments_node| {
15855                            let mut cursor = arguments_node.walk();
15856                            arguments_node
15857                                .named_children(&mut cursor)
15858                                .filter(|child| !child.is_extra() && child.kind() != "comment")
15859                                .collect::<Vec<_>>()
15860                        })
15861                        .unwrap_or_default();
15862                    work.push(Work::BuildGeneric {
15863                        argument_count: arguments.len(),
15864                    });
15865                    work.extend(arguments.into_iter().rev().map(Work::Visit));
15866                    work.push(Work::Visit(name_node));
15867                }
15868                "qualified_identifier"
15869                | "scoped_identifier"
15870                | "scoped_type_identifier"
15871                | "type_identifier"
15872                | "field_identifier"
15873                | "identifier"
15874                | "namespace_identifier"
15875                | "primitive_type" => {
15876                    values.push(builder.named(cpp_structured_named_type(
15877                        current,
15878                        source,
15879                        lexical_scope,
15880                    )?)?);
15881                }
15882                _ => {
15883                    let child = current.child_by_field_name("type").or_else(|| {
15884                        (current.named_child_count() == 1)
15885                            .then(|| current.named_child(0))
15886                            .flatten()
15887                    })?;
15888                    work.push(Work::Visit(child));
15889                }
15890            },
15891            Work::Wrap(wrapper) => {
15892                let root = values.pop()?;
15893                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
15894            }
15895            Work::ApplyWrappers(wrappers) => {
15896                let mut root = values.pop()?;
15897                for wrapper in wrappers.into_iter().rev() {
15898                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
15899                }
15900                values.push(root);
15901            }
15902            Work::BuildGeneric { argument_count } => {
15903                let value_count = argument_count.checked_add(1)?;
15904                let start = values.len().checked_sub(value_count)?;
15905                let mut built = values.split_off(start);
15906                let base = built.remove(0);
15907                values.push(builder.generic(base, built)?);
15908            }
15909        }
15910    }
15911    (values.len() == 1)
15912        .then(|| values.pop())
15913        .flatten()
15914        .and_then(|root| builder.finish(root))
15915}
15916
15917fn cpp_structured_named_type(
15918    node: Node<'_>,
15919    source: &str,
15920    lexical_scope: &[String],
15921) -> Option<StructuredTypeName> {
15922    let path = cpp_structured_type_path(node, source)?;
15923    let absolute = node.child_by_field_name("scope").is_none()
15924        && node.child(0).is_some_and(|child| child.kind() == "::");
15925    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
15926}
15927
15928#[derive(Clone, Copy)]
15929enum CppStructuredTypeWrapper {
15930    Pointer,
15931    LvalueReference,
15932    RvalueReference,
15933    Array,
15934}
15935
15936fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
15937    let mut wrappers = Vec::new();
15938    let mut current = node;
15939    loop {
15940        match current.kind() {
15941            "pointer_declarator" | "abstract_pointer_declarator" => {
15942                wrappers.push(CppStructuredTypeWrapper::Pointer)
15943            }
15944            "reference_declarator" | "abstract_reference_declarator" => {
15945                wrappers.push(cpp_reference_wrapper(current)?);
15946            }
15947            "array_declarator" | "abstract_array_declarator" => {
15948                wrappers.push(CppStructuredTypeWrapper::Array)
15949            }
15950            // An initializer and a grouping parenthesis are not type
15951            // structure. Stopping at one of them dropped every wrapper an
15952            // initialized or parenthesized declarator writes, so
15953            // `std::string &alias = value;` reported the unqualified
15954            // `std::string` while `const std::string &value` as a parameter
15955            // reported the reference it shares.
15956            "init_declarator" | "parenthesized_declarator" => {}
15957            _ => break,
15958        }
15959        let Some(child) = current
15960            .child_by_field_name("declarator")
15961            .or_else(|| current.named_child(0))
15962        else {
15963            break;
15964        };
15965        current = child;
15966    }
15967    Some(wrappers)
15968}
15969
15970fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
15971    node.children(&mut node.walk())
15972        .find_map(|child| match child.kind() {
15973            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
15974            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
15975            _ => None,
15976        })
15977}
15978
15979fn cpp_wrap_structured_type(
15980    identity: StructuredTypeIdentity,
15981    wrapper: CppStructuredTypeWrapper,
15982) -> Option<StructuredTypeIdentity> {
15983    match wrapper {
15984        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
15985        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
15986        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
15987        CppStructuredTypeWrapper::Array => identity.wrap_array(),
15988    }
15989}
15990
15991fn cpp_apply_structured_type_wrappers(
15992    mut identity: StructuredTypeIdentity,
15993    wrappers: Vec<CppStructuredTypeWrapper>,
15994) -> Option<StructuredTypeIdentity> {
15995    for wrapper in wrappers.into_iter().rev() {
15996        identity = cpp_wrap_structured_type(identity, wrapper)?;
15997    }
15998    Some(identity)
15999}
16000
16001fn cpp_wrap_structured_type_node(
16002    builder: &mut StructuredTypeIdentityBuilder,
16003    inner: StructuredTypeNodeId,
16004    wrapper: CppStructuredTypeWrapper,
16005) -> Option<StructuredTypeNodeId> {
16006    match wrapper {
16007        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16008        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16009        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16010        CppStructuredTypeWrapper::Array => builder.array(inner),
16011    }
16012}
16013
16014/// The dot-free name components a qualified C++ name node writes, outermost
16015/// first.
16016///
16017/// The walk reads the grammar's `scope` and `name` fields rather than cutting
16018/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
16019/// shape the grammar does not name yields nothing at all.
16020pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16021    let mut path = Vec::new();
16022    let mut stack = vec![node];
16023    while let Some(current) = stack.pop() {
16024        match current.kind() {
16025            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16026                let component = node_text(current, source).to_string();
16027                if component.is_empty() {
16028                    return None;
16029                }
16030                path.push(component);
16031            }
16032            "template_type" | "dependent_type" => {
16033                stack.push(current.child_by_field_name("name")?);
16034            }
16035            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16036                stack.push(current.child_by_field_name("name")?);
16037                if let Some(scope) = current.child_by_field_name("scope") {
16038                    stack.push(scope);
16039                }
16040            }
16041            _ => return None,
16042        }
16043    }
16044    (!path.is_empty()).then_some(path)
16045}
16046
16047fn cpp_callable_lexical_scope<'tree>(
16048    node: Node<'tree>,
16049    source: &str,
16050    ancestry: &ParentIndex<'tree>,
16051) -> Vec<String> {
16052    let mut groups = Vec::new();
16053    let mut current = ancestry.parent(node);
16054    while let Some(parent) = current {
16055        if matches!(
16056            parent.kind(),
16057            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16058        ) && let Some(name_node) = parent.child_by_field_name("name")
16059            && let Some(components) = cpp_structured_type_path(name_node, source)
16060            && !components.is_empty()
16061        {
16062            groups.push(components);
16063        }
16064        current = ancestry.parent(parent);
16065    }
16066    groups.reverse();
16067    groups.into_iter().flatten().collect()
16068}
16069
16070/// Whether a call on this callable's static target can select another body.
16071///
16072/// C++ says this where the member is *declared*, not where it is defined, and
16073/// that is the whole difficulty of the question: virtual-ness is inherited
16074/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
16075/// `Base::run` is), an out-of-line definition repeats none of it, and whether
16076/// a definition's qualifier names a class or a namespace is a class-table
16077/// question. So a row answers `Closed` only when its own node proves it:
16078///
16079/// * a written `final` is the one specifier that ends the family here, and it
16080///   is the only thing that closes a member of a class that has a base clause;
16081/// * a class-body member is `Closed` otherwise only when its class body writes
16082///   no base clause at all, because a base clause is the only evidence in this
16083///   file that some base may declare this member virtual;
16084/// * anything with a virtual-family boundary, a template head, or a
16085///   preprocessor or error region is `Open`;
16086/// * a qualified declaration outside every class body answers `None`: its
16087///   class body, which is where the fact is written, may be in another file.
16088///
16089/// The analyzer joins the `None` rows with the include-visible class-body rows
16090/// of the same logical callable (#3507), the same join #3509 makes for a
16091/// definition's declared access; a row that keeps `None` after that join stays
16092/// unknown and no dispatch proof may be built on it.
16093fn cpp_callable_dispatch_extensibility<'tree>(
16094    function_declarator: Node<'tree>,
16095    ancestry: &ParentIndex<'tree>,
16096) -> Option<DispatchExtensibility> {
16097    let mut declaration = None;
16098    // The first class body this declarator is written in. The upward walk
16099    // visits it anyway, so only the one step from the body to its class node
16100    // is extra, and only for a class-body member.
16101    let mut class_body = None;
16102    let mut current = Some(function_declarator);
16103    while let Some(node) = current {
16104        match node.kind() {
16105            "template_declaration"
16106            | "preproc_if"
16107            | "preproc_ifdef"
16108            | "preproc_else"
16109            | "preproc_elif"
16110            | "preproc_call"
16111            | "ERROR" => return Some(DispatchExtensibility::Open),
16112            "declaration" | "field_declaration" | "function_definition" => {
16113                declaration.get_or_insert(node);
16114            }
16115            "field_declaration_list" => {
16116                class_body.get_or_insert(node);
16117            }
16118            "translation_unit" => break,
16119            _ => {}
16120        }
16121        current = ancestry.parent(node);
16122    }
16123    let Some(declaration) = declaration else {
16124        return Some(DispatchExtensibility::Open);
16125    };
16126
16127    let mut saw_virtual_boundary = false;
16128    let mut stack = vec![declaration];
16129    while let Some(node) = stack.pop() {
16130        match node.kind() {
16131            "compound_statement" | "field_declaration_list" => continue,
16132            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16133            "virtual"
16134            | "override"
16135            | "virtual_specifier"
16136            | "pure_virtual_clause"
16137            | "template_parameter_list"
16138            | "template_method"
16139            | "template_function"
16140            | "ERROR" => saw_virtual_boundary = true,
16141            _ => {}
16142        }
16143        let mut cursor = node.walk();
16144        stack.extend(node.children(&mut cursor));
16145    }
16146
16147    if saw_virtual_boundary {
16148        return Some(DispatchExtensibility::Open);
16149    }
16150    if let Some(body) = class_body {
16151        // C++ inherits virtual-ness, so a member written without a `virtual`
16152        // keyword is still virtual when a base declares it. Whether some base
16153        // does is not decidable here, so a base clause keeps the answer open.
16154        let has_base_clause = ancestry
16155            .parent(body)
16156            .and_then(cpp_class_like_base_clause)
16157            .is_some();
16158        return Some(if has_base_clause {
16159            DispatchExtensibility::Open
16160        } else {
16161            DispatchExtensibility::Closed
16162        });
16163    }
16164    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16165        return None;
16166    }
16167    // A namespace or file scope callable is reached by name, never through a
16168    // receiver, so no other body can be selected for a call to it.
16169    Some(DispatchExtensibility::Closed)
16170}
16171
16172fn cpp_callable_linkage<'tree>(
16173    declaration: Node<'tree>,
16174    source: &str,
16175    ancestry: &ParentIndex<'tree>,
16176) -> CallableLinkage {
16177    let mut enclosed_by_class = false;
16178    let mut current = ancestry.parent(declaration);
16179    while let Some(node) = current {
16180        if node.kind() == "namespace_definition"
16181            && node
16182                .child_by_field_name("name")
16183                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16184        {
16185            return CallableLinkage::Internal;
16186        }
16187        if matches!(
16188            node.kind(),
16189            "class_specifier" | "struct_specifier" | "union_specifier"
16190        ) {
16191            if node
16192                .child_by_field_name("name")
16193                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16194            {
16195                return CallableLinkage::Internal;
16196            }
16197            enclosed_by_class = true;
16198        }
16199        // An export macro between `class` and the class name can make
16200        // tree-sitter parse the entire class as a function definition. The
16201        // structured recovery proves that this container is a named class
16202        // scope, not a local callable scope.
16203        if node.kind() == "lambda_expression"
16204            || node.kind() == "function_definition"
16205                && !is_recovered_exported_class_container(node, source)
16206        {
16207            return CallableLinkage::Internal;
16208        }
16209        current = ancestry.parent(node);
16210    }
16211
16212    if enclosed_by_class {
16213        return CallableLinkage::External;
16214    }
16215
16216    if cpp_declares_static_storage_class(declaration, source) {
16217        CallableLinkage::Internal
16218    } else {
16219        CallableLinkage::External
16220    }
16221}
16222
16223fn cpp_callable_return_type_text<'tree>(
16224    function_declarator: Node<'tree>,
16225    source: &str,
16226    ancestry: &ParentIndex<'tree>,
16227) -> Option<String> {
16228    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16229        return None;
16230    }
16231    if let Some((return_type, _)) =
16232        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16233    {
16234        let text = normalize_cpp_whitespace(node_text(return_type, source));
16235        return (!text.is_empty()).then_some(text);
16236    }
16237    let mut cursor = function_declarator.walk();
16238    if let Some(trailing) = function_declarator
16239        .named_children(&mut cursor)
16240        .find(|child| child.kind() == "trailing_return_type")
16241        && let Some(type_descriptor) = trailing.named_child(0)
16242    {
16243        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16244        if !text.is_empty() {
16245            return Some(text);
16246        }
16247    }
16248
16249    let mut current = function_declarator;
16250    let mut indirection = String::new();
16251    while let Some(parent) = ancestry.parent(current) {
16252        if matches!(
16253            parent.kind(),
16254            "function_definition" | "declaration" | "field_declaration"
16255        ) {
16256            let type_node = parent.child_by_field_name("type")?;
16257            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16258                return None;
16259            }
16260            let base = normalize_cpp_whitespace(node_text(type_node, source));
16261            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16262        }
16263        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16264            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16265                && parent.named_child_count() == 1
16266                && parent.named_child(0) == Some(current));
16267        if wraps_current_declarator {
16268            match parent.kind() {
16269                "pointer_declarator" => indirection.push('*'),
16270                "reference_declarator" => {
16271                    let reference = parent
16272                        .children(&mut parent.walk())
16273                        .find(|child| !child.is_named())
16274                        .map(|child| node_text(child, source))
16275                        .unwrap_or("&");
16276                    indirection.push_str(reference);
16277                }
16278                "init_declarator" | "parenthesized_declarator" => {}
16279                _ => return None,
16280            }
16281            current = parent;
16282            continue;
16283        }
16284        return None;
16285    }
16286    None
16287}
16288
16289pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> CallableArity {
16290    let mut required = 0;
16291    let mut total = 0;
16292    let mut repeated = false;
16293    for slot in cpp_callable_parameter_slots(parameters_node, source) {
16294        match slot {
16295            CppParameterSlot::Declared {
16296                required: is_required,
16297                ..
16298            } => {
16299                total += 1;
16300                if is_required {
16301                    required += 1;
16302                }
16303            }
16304            CppParameterSlot::OptionalVoid => total += 1,
16305            CppParameterSlot::Ellipsis => repeated = true,
16306        }
16307    }
16308    CallableArity::new(required, total, repeated)
16309}
16310
16311/// One entry of a callable's invocation parameter list, plus an optional
16312/// unnamed `void` declaration retained for the legacy arity result.
16313///
16314/// The list excludes an explicit object parameter and a lone `void`. Every
16315/// derivation of parameter arity or type starts from this same sequence, so
16316/// those views cannot disagree about which parameters exist.
16317#[derive(Clone, Copy)]
16318enum CppParameterSlot<'tree> {
16319    Declared {
16320        parameter: Node<'tree>,
16321        required: bool,
16322    },
16323    OptionalVoid,
16324    Ellipsis,
16325}
16326
16327fn cpp_callable_parameter_slots<'tree>(
16328    parameters_node: Node<'tree>,
16329    source: &str,
16330) -> Vec<CppParameterSlot<'tree>> {
16331    let mut slots = Vec::new();
16332    let mut cursor = parameters_node.walk();
16333    for parameter in parameters_node.children(&mut cursor) {
16334        match parameter.kind() {
16335            "parameter_declaration" | "optional_parameter_declaration" => {
16336                let is_unnamed_void = parameter.child_by_field_name("declarator").is_none()
16337                    && parameter
16338                        .child_by_field_name("type")
16339                        .is_some_and(|type_node| node_text(type_node, source).trim() == "void");
16340                if is_unnamed_void && parameter.kind() == "parameter_declaration" {
16341                    continue;
16342                }
16343                if is_unnamed_void {
16344                    slots.push(CppParameterSlot::OptionalVoid);
16345                    continue;
16346                }
16347                slots.push(CppParameterSlot::Declared {
16348                    parameter,
16349                    required: parameter.kind() == "parameter_declaration",
16350                });
16351            }
16352            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16353                slots.push(CppParameterSlot::Ellipsis);
16354            }
16355            _ => {}
16356        }
16357    }
16358    slots
16359}
16360
16361pub(crate) fn cpp_callable_parameter_types(parameters_node: Node<'_>, source: &str) -> Vec<String> {
16362    cpp_callable_parameter_slots(parameters_node, source)
16363        .into_iter()
16364        .filter_map(|slot| match slot {
16365            CppParameterSlot::Declared { parameter, .. } => {
16366                Some(cpp_parameter_type(parameter, source))
16367            }
16368            CppParameterSlot::Ellipsis => Some("...".to_string()),
16369            CppParameterSlot::OptionalVoid => None,
16370        })
16371        .collect()
16372}
16373
16374/// Return the written name of each callable parameter in invocation order.
16375/// C and C++ permit declarations without names; those slots deliberately stay
16376/// `None` so consumers can distinguish an unnamed formal from a guessed one.
16377pub(crate) fn cpp_callable_parameter_names(
16378    parameters_node: Node<'_>,
16379    source: &str,
16380) -> Vec<Option<String>> {
16381    cpp_callable_parameter_slots(parameters_node, source)
16382        .into_iter()
16383        .filter_map(|slot| match slot {
16384            CppParameterSlot::Declared { parameter, .. } => Some(
16385                parameter
16386                    .child_by_field_name("declarator")
16387                    .and_then(crate::structural::declarator_name_node)
16388                    .and_then(|name| direct_identifier_name(name, source)),
16389            ),
16390            CppParameterSlot::Ellipsis => Some(None),
16391            CppParameterSlot::OptionalVoid => None,
16392        })
16393        .collect()
16394}
16395
16396/// One callable parameter's parser-derived type.
16397///
16398/// A rendered spelling such as `const T&` is a source text, not a type name. A
16399/// consumer that must publish a type into a structured model - a semantic-pack
16400/// type reference, for example - reads this instead.
16401#[derive(Debug, Clone, PartialEq, Eq)]
16402pub enum CppParameterType {
16403    /// The written type reduced to a structured identity. C++ cv-qualifiers
16404    /// have no place in that model and are not represented.
16405    Structured(StructuredTypeIdentity),
16406    /// A `...` pack, which declares no parameter type at all.
16407    Ellipsis,
16408    /// A written type with no structured reduction, such as a macro-obscured,
16409    /// `decltype`-computed, or function-pointer parameter.
16410    Unstructured,
16411}
16412
16413/// The structured type of each invocation parameter, in declaration order.
16414///
16415/// The result is index-parallel with the rendered
16416/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16417/// callable.
16418pub fn cpp_callable_parameter_type_identities<'tree>(
16419    function_declarator: Node<'tree>,
16420    source: &str,
16421    ancestry: &ParentIndex<'tree>,
16422) -> Vec<CppParameterType> {
16423    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16424        return Vec::new();
16425    };
16426    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16427    cpp_callable_parameter_slots(parameters_node, source)
16428        .into_iter()
16429        .filter_map(|slot| match slot {
16430            CppParameterSlot::Ellipsis => Some(CppParameterType::Ellipsis),
16431            CppParameterSlot::Declared { parameter, .. } => Some(
16432                cpp_parameter_type_identity(parameter, source, &lexical_scope)
16433                    .map_or(CppParameterType::Unstructured, CppParameterType::Structured),
16434            ),
16435            CppParameterSlot::OptionalVoid => None,
16436        })
16437        .collect()
16438}
16439
16440/// The parser-derived type of one declared object or parameter.
16441///
16442/// The declaration owns the base `type` field while the individual declarator
16443/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16444/// lets callers distinguish several declarators in one declaration without
16445/// reparsing a rendered signature.
16446pub fn cpp_declaration_type_identity<'tree>(
16447    declaration: Node<'tree>,
16448    declarator: Node<'tree>,
16449    source: &str,
16450    ancestry: &ParentIndex<'tree>,
16451) -> Option<StructuredTypeIdentity> {
16452    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16453    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16454}
16455
16456fn cpp_parameter_type_identity(
16457    parameter: Node<'_>,
16458    source: &str,
16459    lexical_scope: &[String],
16460) -> Option<StructuredTypeIdentity> {
16461    cpp_declaration_type_identity_in_scope(
16462        parameter,
16463        cpp_parameter_declarator(parameter),
16464        source,
16465        lexical_scope,
16466    )
16467}
16468
16469fn cpp_declaration_type_identity_in_scope(
16470    declaration: Node<'_>,
16471    declarator: Option<Node<'_>>,
16472    source: &str,
16473    lexical_scope: &[String],
16474) -> Option<StructuredTypeIdentity> {
16475    let type_node = declaration.child_by_field_name("type")?;
16476    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16477    if let Some(declarator) = declarator {
16478        return cpp_apply_structured_type_wrappers(
16479            identity,
16480            cpp_structured_declarator_wrappers(declarator)?,
16481        );
16482    }
16483    Some(identity)
16484}
16485
16486/// The comparable shape of each invocation parameter, in declaration order.
16487///
16488/// The result is index-parallel with
16489/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16490/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16491/// must treat as evidence of nothing rather than as agreement.
16492pub fn cpp_comparable_parameter_shapes<'tree>(
16493    function_declarator: Node<'tree>,
16494    source: &str,
16495    ancestry: &ParentIndex<'tree>,
16496) -> Vec<CppComparableSlot> {
16497    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
16498        return Vec::new();
16499    };
16500    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16501    cpp_callable_parameter_slots(parameters_node, source)
16502        .into_iter()
16503        .filter_map(|slot| match slot {
16504            CppParameterSlot::Ellipsis => Some(CppComparableSlot::Ellipsis),
16505            CppParameterSlot::Declared { parameter, .. } => Some(
16506                cpp_comparable_parameter(parameter, source, &lexical_scope)
16507                    .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape),
16508            ),
16509            CppParameterSlot::OptionalVoid => None,
16510        })
16511        .collect()
16512}
16513
16514fn cpp_comparable_parameter(
16515    parameter: Node<'_>,
16516    source: &str,
16517    lexical_scope: &[String],
16518) -> Option<CppComparableParameter> {
16519    let type_node = parameter.child_by_field_name("type")?;
16520    let levels = match cpp_parameter_declarator(parameter) {
16521        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16522        None => Vec::new(),
16523    };
16524    let mut shape = cpp_comparable_type_shape(
16525        type_node,
16526        cpp_cv_qualifiers(parameter, source),
16527        levels,
16528        source,
16529        lexical_scope,
16530    )?;
16531    shape.adjust_parameter_top_level();
16532    Some(shape)
16533}
16534
16535/// The `const` and `volatile` qualifiers written as direct named children of
16536/// `node`.
16537///
16538/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16539/// the three places a parameter's qualifiers can be written: on the
16540/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16541/// (inside a template argument list), and on each `pointer_declarator` level
16542/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16543/// and friends - takes no part in C++ type identity, the same filter
16544/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16545fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16546    let mut qualifiers = CppCvQualifiers::default();
16547    let mut cursor = node.walk();
16548    for child in node.named_children(&mut cursor) {
16549        if child.kind() != "type_qualifier" {
16550            continue;
16551        }
16552        match node_text(child, source) {
16553            "const" => qualifiers.konst = true,
16554            "volatile" => qualifiers.volatil = true,
16555            _ => {}
16556        }
16557    }
16558    qualifiers
16559}
16560
16561#[derive(Clone, Copy, Default)]
16562struct CppCvQualifiers {
16563    konst: bool,
16564    volatil: bool,
16565}
16566
16567impl CppCvQualifiers {
16568    fn union(self, other: Self) -> Self {
16569        Self {
16570            konst: self.konst || other.konst,
16571            volatil: self.volatil || other.volatil,
16572        }
16573    }
16574}
16575
16576/// One pointer, reference or array level a declarator chain adds.
16577#[derive(Clone, Copy)]
16578enum CppComparableLevel {
16579    Pointer { konst: bool, volatil: bool },
16580    Reference,
16581    Array,
16582}
16583
16584/// The levels `declarator` adds, outermost written level first.
16585///
16586/// C++ declarator syntax binds inside out: the level written closest to the
16587/// declared name is the outermost type constructor, and tree-sitter nests it
16588/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
16589/// applies in order to reach "array of pointer to int", and the qualifier of
16590/// `int * const *p` is read on the level it was written next to, the inner
16591/// pointer of the resulting type.
16592///
16593/// A declarator chain that names a function type - a function-pointer
16594/// parameter - has no comparable shape and reports `None`, matching the
16595/// structured identity channel.
16596fn cpp_comparable_declarator_levels(
16597    declarator: Node<'_>,
16598    source: &str,
16599) -> Option<Vec<CppComparableLevel>> {
16600    let mut levels = Vec::new();
16601    let mut current = declarator;
16602    loop {
16603        match current.kind() {
16604            "pointer_declarator" | "abstract_pointer_declarator" => {
16605                let qualifiers = cpp_cv_qualifiers(current, source);
16606                levels.push(CppComparableLevel::Pointer {
16607                    konst: qualifiers.konst,
16608                    volatil: qualifiers.volatil,
16609                });
16610            }
16611            "reference_declarator" | "abstract_reference_declarator" => {
16612                levels.push(CppComparableLevel::Reference);
16613            }
16614            "array_declarator" | "abstract_array_declarator" => {
16615                levels.push(CppComparableLevel::Array);
16616            }
16617            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
16618            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
16619            _ => return None,
16620        }
16621        let Some(next) = cpp_nested_declarator(current) else {
16622            return Some(levels);
16623        };
16624        current = next;
16625    }
16626}
16627
16628/// Reduce one written type to a comparable arena.
16629///
16630/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
16631/// two additions: each visited type node carries the cv-qualifiers written on
16632/// it, and declarator levels arrive as a prepared list rather than being
16633/// rediscovered inside the walk.
16634fn cpp_comparable_type_shape(
16635    type_node: Node<'_>,
16636    qualifiers: CppCvQualifiers,
16637    levels: Vec<CppComparableLevel>,
16638    source: &str,
16639    lexical_scope: &[String],
16640) -> Option<CppComparableParameter> {
16641    enum Work<'tree> {
16642        Visit {
16643            node: Node<'tree>,
16644            qualifiers: CppCvQualifiers,
16645        },
16646        ApplyLevels(Vec<CppComparableLevel>),
16647        BuildGeneric {
16648            argument_count: usize,
16649        },
16650    }
16651
16652    let mut nodes: Vec<CppComparableNode> = Vec::new();
16653    let mut values: Vec<usize> = Vec::new();
16654    let mut work = vec![
16655        Work::ApplyLevels(levels),
16656        Work::Visit {
16657            node: type_node,
16658            qualifiers,
16659        },
16660    ];
16661    while let Some(next) = work.pop() {
16662        match next {
16663            Work::Visit { node, qualifiers } => match node.kind() {
16664                "type_descriptor" => {
16665                    let inner_type = node
16666                        .child_by_field_name("type")
16667                        .or_else(|| node.named_child(0))?;
16668                    let mut cursor = node.walk();
16669                    let declarator = node.child_by_field_name("declarator").or_else(|| {
16670                        node.named_children(&mut cursor).find(|child| {
16671                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
16672                        })
16673                    });
16674                    let levels = match declarator {
16675                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16676                        None => Vec::new(),
16677                    };
16678                    work.push(Work::ApplyLevels(levels));
16679                    work.push(Work::Visit {
16680                        node: inner_type,
16681                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
16682                    });
16683                }
16684                "sized_type_specifier" => {
16685                    // `unsigned char` is one primitive type whose components are
16686                    // partly unnamed tokens, so the whole specifier is its own
16687                    // name component. Reducing it to the `type` child would make
16688                    // `f(unsigned char)` and `f(char)` compare equal.
16689                    let name = StructuredTypeName::new(
16690                        vec![normalize_cpp_whitespace(node_text(node, source))],
16691                        lexical_scope.to_vec(),
16692                        false,
16693                    )?;
16694                    values.push(cpp_push_comparable_node(
16695                        &mut nodes,
16696                        CppComparableNode::Named {
16697                            name,
16698                            primitive: true,
16699                            konst: qualifiers.konst,
16700                            volatil: qualifiers.volatil,
16701                        },
16702                    ));
16703                }
16704                "qualified_identifier"
16705                | "scoped_identifier"
16706                | "scoped_type_identifier"
16707                | "type_identifier"
16708                | "field_identifier"
16709                | "identifier"
16710                | "namespace_identifier"
16711                | "primitive_type"
16712                | "template_type" => {
16713                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
16714                    values.push(cpp_push_comparable_node(
16715                        &mut nodes,
16716                        CppComparableNode::Named {
16717                            name,
16718                            primitive: node.kind() == "primitive_type",
16719                            konst: qualifiers.konst,
16720                            volatil: qualifiers.volatil,
16721                        },
16722                    ));
16723                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
16724                        let mut cursor = arguments_node.walk();
16725                        let arguments = arguments_node
16726                            .named_children(&mut cursor)
16727                            .filter(|child| !child.is_extra() && child.kind() != "comment")
16728                            .collect::<Vec<_>>();
16729                        work.push(Work::BuildGeneric {
16730                            argument_count: arguments.len(),
16731                        });
16732                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
16733                            node: argument,
16734                            qualifiers: CppCvQualifiers::default(),
16735                        }));
16736                    }
16737                }
16738                _ => {
16739                    let inner = node.child_by_field_name("type").or_else(|| {
16740                        (node.named_child_count() == 1)
16741                            .then(|| node.named_child(0))
16742                            .flatten()
16743                    })?;
16744                    work.push(Work::Visit {
16745                        node: inner,
16746                        qualifiers,
16747                    });
16748                }
16749            },
16750            Work::ApplyLevels(levels) => {
16751                let mut root = values.pop()?;
16752                for level in levels {
16753                    let node = match level {
16754                        CppComparableLevel::Pointer { konst, volatil } => {
16755                            CppComparableNode::Pointer {
16756                                inner: root,
16757                                konst,
16758                                volatil,
16759                            }
16760                        }
16761                        CppComparableLevel::Reference => {
16762                            CppComparableNode::Reference { inner: root }
16763                        }
16764                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
16765                    };
16766                    root = cpp_push_comparable_node(&mut nodes, node);
16767                }
16768                values.push(root);
16769            }
16770            Work::BuildGeneric { argument_count } => {
16771                let value_count = argument_count.checked_add(1)?;
16772                let start = values.len().checked_sub(value_count)?;
16773                let mut built = values.split_off(start);
16774                let base = built.remove(0);
16775                values.push(cpp_push_comparable_node(
16776                    &mut nodes,
16777                    CppComparableNode::Generic {
16778                        base,
16779                        arguments: built,
16780                    },
16781                ));
16782            }
16783        }
16784    }
16785    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
16786    debug_assert_eq!(
16787        root,
16788        nodes.len().saturating_sub(1),
16789        "comparable nodes are appended in post-order, so the root is the last one"
16790    );
16791    Some(CppComparableParameter::new(nodes, root))
16792}
16793
16794fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
16795    nodes.push(node);
16796    nodes.len() - 1
16797}
16798
16799/// The template argument list of the name `node` terminates in, if any.
16800///
16801/// `std::vector<int>` writes its arguments on the `name` of a qualified
16802/// identifier, so a walk that stopped at the qualified node would reduce
16803/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
16804fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
16805    let mut current = node;
16806    loop {
16807        match current.kind() {
16808            "template_type" => return current.child_by_field_name("arguments"),
16809            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16810                current = current.child_by_field_name("name")?;
16811            }
16812            _ => return None,
16813        }
16814    }
16815}
16816
16817/// The callable declarator of the declaration that covers `start_byte`.
16818///
16819/// A consumer that holds a declaration's recorded byte position rather than its
16820/// syntax node - external header extraction, for instance - uses this to reach
16821/// the same `function_declarator` the declaration walk read.
16822pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
16823    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
16824    loop {
16825        if matches!(
16826            current.kind(),
16827            "declaration" | "field_declaration" | "function_definition"
16828        ) && let Some(declarator) = current
16829            .child_by_field_name("declarator")
16830            .and_then(extract_function_declarator)
16831        {
16832            return Some(declarator);
16833        }
16834        current = current.parent()?;
16835    }
16836}
16837
16838fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
16839    let mut labels = Vec::new();
16840    let mut cursor = parameters_node.walk();
16841    for child in parameters_node.children(&mut cursor) {
16842        match child.kind() {
16843            "parameter_declaration" | "optional_parameter_declaration" => {
16844                if let Some(name_node) = child
16845                    .child_by_field_name("declarator")
16846                    .and_then(cpp_declarator_label_node)
16847                {
16848                    labels.push(name_node);
16849                } else {
16850                    labels.push(child);
16851                }
16852            }
16853            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
16854                labels.push(child);
16855            }
16856            _ => {}
16857        }
16858    }
16859    labels
16860}
16861
16862fn cpp_signature_search_start<'tree>(
16863    signature: &str,
16864    function_declarator: Node<'tree>,
16865    source: &str,
16866    ancestry: &ParentIndex<'tree>,
16867) -> usize {
16868    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
16869        return 0;
16870    };
16871    let raw = node_text(enclosing, source);
16872    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
16873    let offset = function_declarator
16874        .start_byte()
16875        .saturating_sub(enclosing.start_byte())
16876        .saturating_sub(leading_trim_bytes);
16877    offset.min(signature.len())
16878}
16879
16880fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
16881    match node.kind() {
16882        "identifier" | "field_identifier" => Some(node),
16883        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
16884            .child_by_field_name("declarator")
16885            .or_else(|| last_named_child(node))
16886            .and_then(cpp_declarator_label_node),
16887        "array_declarator" => node
16888            .child_by_field_name("declarator")
16889            .and_then(cpp_declarator_label_node),
16890        "function_declarator" => node
16891            .child_by_field_name("declarator")
16892            .or_else(|| node.child_by_field_name("name"))
16893            .or_else(|| last_named_child(node))
16894            .and_then(cpp_declarator_label_node),
16895        _ => None,
16896    }
16897}
16898
16899fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
16900    let base_type = parameter
16901        .child_by_field_name("type")
16902        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
16903        .unwrap_or_default();
16904    let declarator = cpp_parameter_declarator(parameter);
16905    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
16906    // are discarded, so `f(const int)` and `f(int)` declare one function. A
16907    // qualifier written next to the parameter's type is only top-level when
16908    // the declarator adds no indirection; behind a pointer, reference or array
16909    // declarator the same qualifier belongs to the pointee, referent or
16910    // element and keeps distinguishing the type (#1827).
16911    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
16912    let mut cursor = parameter.walk();
16913    let qualifiers = parameter
16914        .named_children(&mut cursor)
16915        .filter(|child| child.kind() == "type_qualifier")
16916        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
16917        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
16918        .collect::<Vec<_>>()
16919        .join(" ");
16920    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
16921        (true, _) => base_type,
16922        (_, true) => qualifiers,
16923        (false, false) => format!("{qualifiers} {base_type}"),
16924    };
16925    let declarator_suffix = declarator
16926        .map(|node| cpp_declarator_suffix_without_name(node, source))
16927        .unwrap_or_default();
16928
16929    let combined = if type_text.is_empty() {
16930        declarator_suffix
16931    } else if declarator_suffix.is_empty() {
16932        type_text
16933    } else {
16934        format!("{type_text} {declarator_suffix}")
16935    };
16936    normalize_cpp_type_text(&combined)
16937}
16938
16939fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
16940    parameter.child_by_field_name("declarator").or_else(|| {
16941        // Some unnamed prototype parameters expose their abstract declarator
16942        // as a direct named child without the grammar's `declarator` field.
16943        // Recover only the structured abstract-declarator node; the parameter's
16944        // type and qualifiers are distinct children and must not be guessed from
16945        // source text.
16946        cpp_first_abstract_declarator_child(parameter)
16947    })
16948}
16949
16950/// Whether a parameter's declarator chain adds indirection - a pointer,
16951/// reference, array or function declarator - to the parameter's written type.
16952pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
16953    let mut current = Some(declarator);
16954    while let Some(node) = current {
16955        if cpp_is_member_pointer_declarator(node) {
16956            return true;
16957        }
16958        if matches!(
16959            node.kind(),
16960            "pointer_declarator"
16961                | "abstract_pointer_declarator"
16962                | "reference_declarator"
16963                | "abstract_reference_declarator"
16964                | "array_declarator"
16965                | "abstract_array_declarator"
16966                | "function_declarator"
16967                | "abstract_function_declarator"
16968        ) {
16969            return true;
16970        }
16971        current = cpp_nested_declarator(node);
16972    }
16973    false
16974}
16975
16976fn is_cpp_abstract_declarator(kind: &str) -> bool {
16977    matches!(
16978        kind,
16979        "abstract_pointer_declarator"
16980            | "abstract_reference_declarator"
16981            | "abstract_array_declarator"
16982            | "abstract_function_declarator"
16983            | "abstract_parenthesized_declarator"
16984    )
16985}
16986
16987fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
16988    let mut cursor = node.walk();
16989    node.named_children(&mut cursor)
16990        .find(|child| is_cpp_abstract_declarator(child.kind()))
16991}
16992
16993fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
16994    node.child_by_field_name("declarator").or_else(|| {
16995        if is_cpp_abstract_declarator(node.kind()) {
16996            cpp_first_abstract_declarator_child(node)
16997        } else {
16998            // Named declarators historically use their last named child when
16999            // tree-sitter omits the field. Keep that broad fallback for
17000            // attributed, variadic, and recovered named shapes.
17001            last_named_child(node)
17002        }
17003    })
17004}
17005
17006pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17007    matches!(
17008        node.kind(),
17009        "reference_declarator" | "abstract_reference_declarator"
17010    ) && node
17011        .children(&mut node.walk())
17012        .any(|child| child.kind() == "&&")
17013}
17014
17015/// Whether `node` is a pointer-to-member declarator, `Left::*cb` or `Left::*`.
17016/// brokk-tree-sitter-cpp 0.24.7 reads one as a `qualified_identifier` whose
17017/// `name` is a pointer declarator, possibly behind further scopes
17018/// (`A::B::*p`), with the owning class in `scope`.
17019fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17020    let mut current = node;
17021    while current.kind() == "qualified_identifier" {
17022        let Some(name) = current.child_by_field_name("name") else {
17023            return false;
17024        };
17025        if matches!(
17026            name.kind(),
17027            "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17028        ) {
17029            return true;
17030        }
17031        current = name;
17032    }
17033    false
17034}
17035
17036fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17037    match node.kind() {
17038        "identifier" | "field_identifier" => String::new(),
17039        // `Left::*cb` keeps its owning class, so `void (Left::*)()` and
17040        // `void (Right::*)()` stay distinct overloads (#6902 item 6).
17041        "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17042            let scope = node
17043                .child_by_field_name("scope")
17044                .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17045                .unwrap_or_default();
17046            let name = node
17047                .child_by_field_name("name")
17048                .map(|name| cpp_declarator_suffix_without_name(name, source))
17049                .unwrap_or_default();
17050            format!("{scope}::{name}")
17051        }
17052        "pointer_declarator" | "abstract_pointer_declarator" => {
17053            let inner = cpp_nested_declarator(node)
17054                .map(|child| cpp_declarator_suffix_without_name(child, source))
17055                .unwrap_or_default();
17056            format!("*{inner}")
17057        }
17058        "reference_declarator" | "abstract_reference_declarator" => {
17059            let inner = cpp_nested_declarator(node)
17060                .map(|child| cpp_declarator_suffix_without_name(child, source))
17061                .unwrap_or_default();
17062            let reference = if reference_declarator_is_rvalue(node) {
17063                "&&"
17064            } else {
17065                "&"
17066            };
17067            format!("{reference}{inner}")
17068        }
17069        "array_declarator" | "abstract_array_declarator" => {
17070            let inner = cpp_nested_declarator(node)
17071                .map(|child| cpp_declarator_suffix_without_name(child, source))
17072                .unwrap_or_default();
17073            let size = node
17074                .child_by_field_name("size")
17075                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17076                .unwrap_or_default();
17077            format!("{inner}[{size}]")
17078        }
17079        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17080            let inner = cpp_nested_declarator(node);
17081            inner
17082                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17083                .unwrap_or_default()
17084        }
17085        "function_declarator" | "abstract_function_declarator" => {
17086            let inner = cpp_nested_declarator(node)
17087                .map(|child| cpp_declarator_suffix_without_name(child, source))
17088                .unwrap_or_default();
17089            let params = node
17090                .child_by_field_name("parameters")
17091                .map(|child| cpp_parameter_signature(child, source))
17092                .unwrap_or_else(|| "()".to_string());
17093            format!("{inner}{params}")
17094        }
17095        _ => {
17096            let text = normalize_cpp_whitespace(node_text(node, source));
17097            let name = extract_declarator_name(node, source);
17098            if name.is_empty() {
17099                text
17100            } else {
17101                text.replace(&name, "").trim().to_string()
17102            }
17103        }
17104    }
17105}
17106
17107fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17108    collapse_cpp_whitespace(
17109        suffix
17110            .trim()
17111            .trim_start_matches("->")
17112            .trim_start_matches('{')
17113            .trim_end_matches(';'),
17114    )
17115}
17116
17117pub fn normalize_cpp_whitespace(value: &str) -> String {
17118    collapse_cpp_whitespace(value)
17119}
17120
17121fn normalize_cpp_type_text(value: &str) -> String {
17122    collapse_cpp_whitespace(value)
17123        .replace(", ", ",")
17124        .replace(" <", "<")
17125        .replace("< ", "<")
17126        .replace(" >", ">")
17127}
17128
17129fn collapse_cpp_whitespace(value: &str) -> String {
17130    let mut result = String::new();
17131    let mut prev_space = false;
17132    for ch in value.chars() {
17133        if ch.is_whitespace() {
17134            if !prev_space {
17135                result.push(' ');
17136            }
17137            prev_space = true;
17138        } else {
17139            result.push(ch);
17140            prev_space = false;
17141        }
17142    }
17143    result.trim().to_string()
17144}
17145
17146fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17147    child_by_field_or_named_child_of_kinds(
17148        node,
17149        "body",
17150        &[
17151            "declaration_list",
17152            "field_declaration_list",
17153            "enumerator_list",
17154        ],
17155    )
17156}
17157
17158/// Return a class body's actual closing brace when the parser supplied one.
17159///
17160/// A malformed namespace sentinel can leave a class node carrying unrelated
17161/// parser errors even though its own class body is complete.  `has_error()` is
17162/// therefore too coarse an admission predicate for sentinel ownership.  The
17163/// body list, however, exposes the opening and closing punctuation directly;
17164/// a real (non-missing) final `}` proves that the class did not borrow the
17165/// enclosing namespace's close.  Requiring the body to end before its parent
17166/// container also rejects a recovered node whose body swallowed that outer
17167/// boundary.
17168fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17169    if !matches!(
17170        node.kind(),
17171        "class_specifier" | "struct_specifier" | "union_specifier"
17172    ) {
17173        return None;
17174    }
17175    let body = cpp_body_node(node)?;
17176    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17177        return None;
17178    }
17179    let open = body.child(0)?;
17180    let close = body.child(body.child_count().checked_sub(1)?)?;
17181    if open.kind() != "{"
17182        || open.is_missing()
17183        || close.kind() != "}"
17184        || close.is_missing()
17185        || close.end_byte() != body.end_byte()
17186        || body.end_byte() > node.end_byte()
17187        || node
17188            .parent()
17189            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17190    {
17191        return None;
17192    }
17193    Some(close)
17194}
17195
17196fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17197    let mut stack = vec![node];
17198    while let Some(node) = stack.pop() {
17199        if node.kind() == "namespace_definition" {
17200            return true;
17201        }
17202        push_named_children_reversed(node, &mut stack);
17203    }
17204    false
17205}
17206
17207struct CppNestedNamespaceSentinel<'tree> {
17208    function: Node<'tree>,
17209    body: Node<'tree>,
17210    namespace_components: Vec<String>,
17211}
17212
17213/// Owned structural recovery metadata for a namespace-sentinel region.
17214///
17215/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
17216/// instead of the namespace/class scopes that the declaration visitor restores.
17217/// The inverted usage walk has the original CST, so it needs the same ownership
17218/// evidence without borrowing parser nodes across its file scan.  Keep this
17219/// descriptor deliberately source-range based: callers can match a reference
17220/// node by containment and then resolve its structured type spelling in the
17221/// recovered class scope.
17222#[derive(Debug, Clone)]
17223pub struct CppSentinelRecoveredOwner {
17224    pub range: Range,
17225    /// Start of the qualified owner name (`btree<P>::method`).  A leading
17226    /// return type before this byte is looked up from the namespace; parameters,
17227    /// trailing returns, and the body use the member owner scope.
17228    pub owner_name_start_byte: usize,
17229    /// Number of leading components belonging to the namespace rather than
17230    /// the qualified class owner.  A leading return type is looked up before
17231    /// every owner component, not merely before the innermost class.
17232    pub namespace_component_count: usize,
17233    pub scope_components: Vec<String>,
17234}
17235
17236#[derive(Debug, Clone)]
17237pub struct CppSentinelRecoveredClass {
17238    pub namespace_range: Range,
17239    pub namespace_scope_components: Vec<String>,
17240    pub class_range: Range,
17241    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
17242    pub scope_components: Vec<String>,
17243    /// Qualified out-of-line member definitions owned by this class.  Their
17244    /// ranges may extend beyond `class_range` when the malformed sentinel
17245    /// swallowed the namespace close and left definitions as function siblings.
17246    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17247}
17248
17249/// Resolve the lexical scope restored for a node in a malformed
17250/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
17251/// outrank class spans, which in turn outrank the surviving namespace body.
17252/// The class ancestor suffix is recovered from the original CST so nested
17253/// members keep their complete `Outer::Inner` owner chain.
17254pub fn cpp_sentinel_recovered_scope_for_node(
17255    node: Node<'_>,
17256    source: &str,
17257    recovered_classes: &[CppSentinelRecoveredClass],
17258) -> Option<Vec<String>> {
17259    let contains =
17260        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17261    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17262    for recovered in recovered_classes {
17263        for owner in recovered
17264            .owner_ranges
17265            .iter()
17266            .filter(|owner| contains(owner.range))
17267        {
17268            let replace = best_owner.is_none_or(|existing| {
17269                owner.range.end_byte.saturating_sub(owner.range.start_byte)
17270                    < existing
17271                        .range
17272                        .end_byte
17273                        .saturating_sub(existing.range.start_byte)
17274            });
17275            if replace {
17276                best_owner = Some(owner);
17277            }
17278        }
17279    }
17280    if let Some(owner) = best_owner {
17281        let mut scope = owner.scope_components.clone();
17282        if node.start_byte() < owner.owner_name_start_byte {
17283            scope.truncate(owner.namespace_component_count);
17284        }
17285        return Some(scope);
17286    }
17287
17288    let class = recovered_classes
17289        .iter()
17290        .filter(|recovered| contains(recovered.class_range))
17291        .min_by_key(|recovered| {
17292            recovered
17293                .class_range
17294                .end_byte
17295                .saturating_sub(recovered.class_range.start_byte)
17296        });
17297    let class_scope = class.is_some();
17298    let mut scope = if let Some(class) = class {
17299        class.scope_components.clone()
17300    } else {
17301        let namespace = recovered_classes
17302            .iter()
17303            .filter(|recovered| contains(recovered.namespace_range))
17304            .min_by_key(|recovered| {
17305                recovered
17306                    .namespace_range
17307                    .end_byte
17308                    .saturating_sub(recovered.namespace_range.start_byte)
17309            })?;
17310        let mut scope = namespace.namespace_scope_components.clone();
17311        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17312        let common_prefix = scope
17313            .iter()
17314            .zip(&parser_namespace)
17315            .take_while(|(recovered, parser)| recovered == parser)
17316            .count();
17317        scope.extend(parser_namespace.into_iter().skip(common_prefix));
17318        scope
17319    };
17320    if class_scope {
17321        let mut ancestor_components = Vec::new();
17322        let mut ancestor = node.parent();
17323        while let Some(current) = ancestor {
17324            if matches!(
17325                current.kind(),
17326                "class_specifier" | "struct_specifier" | "union_specifier"
17327            ) && let Some(name) = current.child_by_field_name("name")
17328                && let Some(name_components) = cpp_name_components(name, source)
17329            {
17330                ancestor_components.push(
17331                    name_components
17332                        .into_iter()
17333                        .map(|component| component.name)
17334                        .collect::<Vec<_>>(),
17335                );
17336            }
17337            ancestor = current.parent();
17338        }
17339        ancestor_components.reverse();
17340        let base_len = scope.len();
17341        for component in ancestor_components.into_iter().flatten() {
17342            if scope.len() >= base_len && scope.last() == Some(&component) {
17343                continue;
17344            }
17345            scope.push(component);
17346        }
17347    }
17348    Some(scope)
17349}
17350
17351struct CppSentinelFragmentedClassTail<'tree> {
17352    class_node: Node<'tree>,
17353    template_node: Option<Node<'tree>>,
17354    name: String,
17355    raw_supertypes: Option<Vec<String>>,
17356    fragmented: FragmentedExportBody,
17357    consumed_start: usize,
17358    /// The original node whose direct children after the class open are the
17359    /// class's members: the body of a class node, or the class-prefix `ERROR`
17360    /// itself when the class keyword, name, and `{` are its direct tokens.
17361    member_container: Node<'tree>,
17362}
17363
17364struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17365    name: String,
17366    open: Node<'tree>,
17367    raw_supertypes: Option<Vec<String>>,
17368}
17369
17370struct CppSentinelDirectBodyClassRegion {
17371    namespace_components: Vec<String>,
17372    class_start: usize,
17373    class_start_line: usize,
17374    class_close_end: usize,
17375    class_close_line: usize,
17376    name: String,
17377}
17378
17379fn cpp_sentinel_body_class_candidate<'tree>(
17380    child: Node<'tree>,
17381) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17382    if matches!(
17383        child.kind(),
17384        "class_specifier" | "struct_specifier" | "union_specifier"
17385    ) {
17386        return Some((child, None));
17387    }
17388    if child.kind() != "template_declaration" {
17389        if child.kind() == "declaration" {
17390            return Some((first_class_like_child(child)?, None));
17391        }
17392        return None;
17393    }
17394    let mut cursor = child.walk();
17395    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17396        if matches!(
17397            candidate.kind(),
17398            "class_specifier" | "struct_specifier" | "union_specifier"
17399        ) {
17400            Some(candidate)
17401        } else if candidate.kind() == "declaration" {
17402            first_class_like_child(candidate)
17403        } else {
17404            None
17405        }
17406    })?;
17407    Some((class_node, Some(child)))
17408}
17409
17410/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17411/// namespace-sentinel body when a later member macro ends the bogus sentinel
17412/// function before the real class close. The anonymous class/open tokens and
17413/// direct identifier are the structural proof; a retained direct close would
17414/// be an ordinary malformed class rather than the fragmented tail handled here.
17415fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17416    node: Node<'tree>,
17417    source: &str,
17418) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17419    let name = malformed_class_error_owner_name(node, source)?;
17420    let mut cursor = node.walk();
17421    let children = node.children(&mut cursor).collect::<Vec<_>>();
17422    let keyword = children.first()?;
17423    let open_index = children.iter().position(|child| child.kind() == "{")?;
17424    if children[open_index + 1..]
17425        .iter()
17426        .any(|child| child.kind() == "}")
17427    {
17428        return None;
17429    }
17430    let raw_supertypes =
17431        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17432    Some(CppSentinelFragmentedClassErrorPrefix {
17433        name,
17434        open: children[open_index],
17435        raw_supertypes,
17436    })
17437}
17438
17439fn cpp_sentinel_direct_body_class_candidate<'tree>(
17440    child: Node<'tree>,
17441) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17442    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17443        return Some(candidate);
17444    }
17445    if child.kind() != "template_declaration" {
17446        return None;
17447    }
17448    let mut cursor = child.walk();
17449    let wrapper = child
17450        .named_children(&mut cursor)
17451        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17452    Some((first_class_like_child(wrapper)?, Some(child)))
17453}
17454
17455fn cpp_sentinel_direct_namespace_components(
17456    function: Node<'_>,
17457    body: Node<'_>,
17458    source: &str,
17459) -> Option<Vec<String>> {
17460    let mut cursor = function.walk();
17461    let children = function
17462        .named_children(&mut cursor)
17463        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17464        .collect::<Vec<_>>();
17465    let sentinel_index = children.iter().rposition(|child| {
17466        direct_identifier_name(*child, source)
17467            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17468    })?;
17469    let mut identifiers = Vec::new();
17470    let mut stack = children[sentinel_index + 1..]
17471        .iter()
17472        .rev()
17473        .copied()
17474        .collect::<Vec<_>>();
17475    while let Some(current) = stack.pop() {
17476        if let Some(name) = direct_identifier_name(current, source) {
17477            identifiers.push(name);
17478            continue;
17479        }
17480        let mut cursor = current.walk();
17481        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17482        stack.extend(children.into_iter().rev());
17483    }
17484    let [keyword, namespace] = identifiers.as_slice() else {
17485        return None;
17486    };
17487    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17488        .then(|| vec![namespace.clone()])
17489}
17490
17491fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17492    let mut sibling = class_semicolon.next_named_sibling();
17493    let namespace_close = loop {
17494        let Some(current) = sibling else {
17495            return false;
17496        };
17497        sibling = current.next_named_sibling();
17498        if current.kind() != "comment" {
17499            break current;
17500        }
17501    };
17502    if !cpp_is_stray_close_brace(namespace_close, source) {
17503        return false;
17504    }
17505    loop {
17506        let Some(current) = sibling else {
17507            return false;
17508        };
17509        sibling = current.next_named_sibling();
17510        if current.kind() == "comment" {
17511            continue;
17512        }
17513        return direct_identifier_name(current, source)
17514            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17515    }
17516}
17517
17518fn cpp_sentinel_macro_body_class_region<'tree>(
17519    node: Node<'tree>,
17520    source: &str,
17521    ancestry: &ParentIndex<'tree>,
17522) -> Option<CppSentinelDirectBodyClassRegion> {
17523    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17524        return None;
17525    };
17526    if node.kind() != "function_definition" || !node.has_error() {
17527        return None;
17528    }
17529    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17530    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17531    let mut cursor = body.walk();
17532    let candidates = body
17533        .named_children(&mut cursor)
17534        .filter_map(cpp_sentinel_direct_body_class_candidate)
17535        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17536        .collect::<Vec<_>>();
17537    let [(class_node, template_node)] = candidates.as_slice() else {
17538        return None;
17539    };
17540    let original_body = cpp_body_node(*class_node)?;
17541    let name = class_like_name(*class_node, source, ancestry)?;
17542    if name.is_empty() {
17543        return None;
17544    }
17545
17546    let mut sibling = node.next_named_sibling();
17547    let (class_close_start, class_close_end, class_close_line) = loop {
17548        let current = sibling?;
17549        let next = current.next_named_sibling();
17550        if cpp_is_stray_close_brace(current, source)
17551            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17552        {
17553            let semicolon = next.expect("checked above");
17554            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17555                return None;
17556            }
17557            break (
17558                current.start_byte(),
17559                semicolon.end_byte(),
17560                semicolon.end_position().row + 1,
17561            );
17562        }
17563        sibling = next;
17564    };
17565    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17566    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17567    let root = tree.root_node();
17568    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
17569    // The region reparse is its own tree, so it needs its own parent index;
17570    // the caller's index answers nothing about these nodes.
17571    let reparsed_ancestry = ParentIndex::new(root);
17572    let reparsed =
17573        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
17574    if reparsed.name != name
17575        || reparsed.declaration_node.start_byte() != class_node.start_byte()
17576        || reparsed.body.start_byte() != original_body.start_byte()
17577        || class_close_start <= reparsed.body.end_byte()
17578        || class_close_end <= class_node.end_byte()
17579    {
17580        return None;
17581    }
17582    Some(CppSentinelDirectBodyClassRegion {
17583        namespace_components,
17584        class_start: reparse_start,
17585        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
17586            node.start_position().row + 1
17587        }),
17588        class_close_end,
17589        class_close_line,
17590        name,
17591    })
17592}
17593
17594/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
17595/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
17596///
17597/// The parser puts the namespace opener and the malformed function in one root
17598/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
17599/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
17600/// the malformed function must begin with an all-caps type, then an ERROR whose
17601/// sole identifier is `namespace`, followed by the inner namespace identifier
17602/// and a compound body; and that body must contain a complete named class or a
17603/// structurally fragmented class prefix. A text reparse cannot prove any of
17604/// those ownership boundaries.
17605fn cpp_nested_namespace_sentinel<'tree>(
17606    node: Node<'tree>,
17607    source: &str,
17608    ancestry: &ParentIndex<'tree>,
17609) -> Option<CppNestedNamespaceSentinel<'tree>> {
17610    // The shape checks below are the admission proof: a `type_identifier`
17611    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
17612    // parser damage on its own, and under the macro-declaration grammar the
17613    // `macro_modifier` wrapper and the keyword in the type field are each
17614    // admitted only where the parse would otherwise contain an error.
17615    let (function, mut namespace_components) = if node.kind() == "ERROR" {
17616        let mut cursor = node.walk();
17617        let functions = node
17618            .named_children(&mut cursor)
17619            .filter(|child| child.kind() == "function_definition")
17620            .collect::<Vec<_>>();
17621        let [function] = functions.as_slice() else {
17622            return None;
17623        };
17624        let mut cursor = node.walk();
17625        let children = node.children(&mut cursor).collect::<Vec<_>>();
17626        let function_index = children
17627            .iter()
17628            .position(|child| same_node(*child, *function))?;
17629        let [outer_keyword, outer_name, outer_open] =
17630            children.get(function_index.checked_sub(3)?..function_index)?
17631        else {
17632            return None;
17633        };
17634        if outer_keyword.kind() != "namespace"
17635            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
17636            || outer_open.kind() != "{"
17637        {
17638            return None;
17639        }
17640        (
17641            *function,
17642            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
17643        )
17644    } else if node.kind() == "function_definition" {
17645        let declaration_list = node.parent()?;
17646        let namespace = declaration_list.parent()?;
17647        if declaration_list.kind() != "declaration_list"
17648            || namespace.kind() != "namespace_definition"
17649            || namespace.child_by_field_name("body") != Some(declaration_list)
17650        {
17651            return None;
17652        }
17653        (node, Vec::new())
17654    } else {
17655        return None;
17656    };
17657
17658    let mut cursor = function.walk();
17659    let named = function
17660        .named_children(&mut cursor)
17661        .filter(|child| child.kind() != "comment")
17662        .collect::<Vec<_>>();
17663    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
17664        return None;
17665    };
17666    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
17667    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
17668    // the same token is wrapped in.
17669    let sentinel = if first_type.kind() == "type_identifier" {
17670        normalize_cpp_whitespace(node_text(*first_type, source))
17671    } else {
17672        let identifier = macro_modifier_identifier(*first_type)?;
17673        normalize_cpp_whitespace(node_text(identifier, source))
17674    };
17675    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17676        return None;
17677    }
17678    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
17679    // grammar, or the function's bogus type field under the macro-declaration
17680    // grammar, where a keyword cannot be a type.
17681    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
17682        inner_error.named_child(0)?
17683    } else if inner_error.kind() == "type_identifier" {
17684        *inner_error
17685    } else {
17686        return None;
17687    };
17688    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
17689        return None;
17690    }
17691    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
17692        return None;
17693    }
17694    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
17695    if inner_name.is_empty() || body.kind() != "compound_statement" {
17696        return None;
17697    }
17698    namespace_components.push(inner_name);
17699
17700    let mut cursor = body.walk();
17701    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17702        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17703            cpp_body_node(class_node).is_some()
17704                && class_like_name(class_node, source, ancestry)
17705                    .is_some_and(|name| !name.is_empty())
17706        })
17707    });
17708    if !has_complete_class
17709        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
17710    {
17711        return None;
17712    }
17713
17714    Some(CppNestedNamespaceSentinel {
17715        function,
17716        body: *body,
17717        namespace_components,
17718    })
17719}
17720
17721/// Recognize a namespace-begin sentinel directly beneath the translation unit.
17722///
17723/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
17724/// function whose type is the sentinel, whose declarator is the structured
17725/// qualified name `namespace::a::b`, and whose body contains the namespace
17726/// items. Declaration indexing already reparses this bounded region. The
17727/// inverse scanner retains the original tree, so recover the same namespace
17728/// components from the declarator fields for its lexical-scope metadata.
17729fn cpp_root_namespace_sentinel<'tree>(
17730    node: Node<'tree>,
17731    source: &str,
17732    ancestry: &ParentIndex<'tree>,
17733) -> Option<CppNestedNamespaceSentinel<'tree>> {
17734    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
17735        return None;
17736    }
17737    let body = node.child_by_field_name("body")?;
17738    let declarator = node.child_by_field_name("declarator")?;
17739    let mut cursor = node.walk();
17740    let named = node
17741        .named_children(&mut cursor)
17742        .filter(|child| child.kind() != "comment")
17743        .collect::<Vec<_>>();
17744    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
17745        return None;
17746    }
17747    // The sentinel is the bogus return `type` (`type_identifier`), with the
17748    // literal `namespace` keyword leading the qualified declarator -- or,
17749    // under the macro-declaration grammar, a `macro_modifier`, with the
17750    // keyword occupying the type field (a keyword cannot be a type, which is
17751    // the same recovery evidence) and the namespace path as the declarator.
17752    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
17753        [first, second, _] => {
17754            let first_type = node.child_by_field_name("type")?;
17755            if !same_node(*first, first_type)
17756                || first_type.kind() != "type_identifier"
17757                || !same_node(*second, declarator)
17758                || !node.has_error()
17759            {
17760                return None;
17761            }
17762            (
17763                normalize_cpp_whitespace(node_text(first_type, source)),
17764                true,
17765            )
17766        }
17767        [first, second, third, _] => {
17768            let type_node = node.child_by_field_name("type")?;
17769            let identifier = macro_modifier_identifier(*first)?;
17770            if !same_node(*second, type_node)
17771                || type_node.kind() != "type_identifier"
17772                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
17773                || !same_node(*third, declarator)
17774            {
17775                return None;
17776            }
17777            (
17778                normalize_cpp_whitespace(node_text(identifier, source)),
17779                false,
17780            )
17781        }
17782        _ => return None,
17783    };
17784    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
17785        return None;
17786    }
17787    if !matches!(
17788        declarator.kind(),
17789        "qualified_identifier" | "identifier" | "namespace_identifier"
17790    ) {
17791        return None;
17792    }
17793    let mut declarator_components = Vec::new();
17794    let mut valid_components = true;
17795    walk_named_tree_preorder(declarator, true, |component| {
17796        if !matches!(
17797            component.kind(),
17798            "identifier" | "namespace_identifier" | "type_identifier"
17799        ) {
17800            return WalkControl::Continue;
17801        }
17802        let Some(component) = canonical_cpp_qualified_component(component, source) else {
17803            valid_components = false;
17804            return WalkControl::Break;
17805        };
17806        declarator_components.push(component.name);
17807        WalkControl::SkipChildren
17808    });
17809    if strip_namespace_keyword {
17810        if !valid_components
17811            || declarator_components.first().map(String::as_str) != Some("namespace")
17812        {
17813            return None;
17814        }
17815        declarator_components.remove(0);
17816    }
17817    let namespace_components = declarator_components;
17818    if !valid_components
17819        || namespace_components.is_empty()
17820        || namespace_components
17821            .iter()
17822            .any(|component| component.is_empty() || cpp_export_macro_token(component))
17823    {
17824        return None;
17825    }
17826
17827    let mut cursor = body.walk();
17828    let has_complete_class = body.named_children(&mut cursor).any(|child| {
17829        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
17830            cpp_body_node(class_node).is_some()
17831                && class_like_name(class_node, source, ancestry)
17832                    .is_some_and(|name| !name.is_empty())
17833        })
17834    });
17835    if !has_complete_class
17836        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
17837    {
17838        return None;
17839    }
17840
17841    Some(CppNestedNamespaceSentinel {
17842        function: node,
17843        body,
17844        namespace_components,
17845    })
17846}
17847
17848/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
17849/// malformed namespace-sentinel function.  The recovery is deliberately
17850/// structural: the class must be a direct body item, its own class node must be
17851/// erroneous and end before a unique anonymous `}` in the enclosing
17852/// declaration-list, and that namespace's next sibling must be a standalone
17853/// `;`.  The complete interior must pass the existing member-shaped reparse
17854/// gate. This avoids source brace scans and does not borrow a close from an
17855/// unrelated later declaration.
17856fn cpp_sentinel_fragmented_class_tail<'tree>(
17857    function: Node<'tree>,
17858    body: Node<'tree>,
17859    source: &str,
17860    ancestry: &ParentIndex<'tree>,
17861) -> Option<CppSentinelFragmentedClassTail<'tree>> {
17862    let mut cursor = body.walk();
17863    let candidates = body
17864        .named_children(&mut cursor)
17865        .filter_map(|child| {
17866            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
17867                let class_body = cpp_body_node(class_node)?;
17868                if !class_node.has_error() {
17869                    return None;
17870                }
17871                let name = class_like_name(class_node, source, ancestry)?;
17872                let raw_supertypes =
17873                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
17874                        .then(|| extract_cpp_supertypes(class_node, source));
17875                return Some((
17876                    class_node,
17877                    template_node,
17878                    name,
17879                    class_body,
17880                    class_body.start_byte().checked_add(1)?,
17881                    raw_supertypes,
17882                    class_body,
17883                ));
17884            }
17885            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
17886            Some((
17887                child,
17888                None,
17889                prefix.name,
17890                prefix.open,
17891                prefix.open.end_byte(),
17892                prefix.raw_supertypes,
17893                child,
17894            ))
17895        })
17896        .collect::<Vec<_>>();
17897    let [
17898        (
17899            class_node,
17900            template_node,
17901            name,
17902            class_body,
17903            reparse_start,
17904            raw_supertypes,
17905            member_container,
17906        ),
17907    ] = candidates.as_slice()
17908    else {
17909        return None;
17910    };
17911    if name.is_empty() {
17912        return None;
17913    }
17914
17915    let (close, semicolon) =
17916        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
17917
17918    let reparse_end = close.start_byte();
17919    if *reparse_start >= reparse_end {
17920        return None;
17921    }
17922    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
17923    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
17924        return None;
17925    }
17926    let class_range = Range {
17927        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17928        end_byte: semicolon.end_byte(),
17929        start_line: template_node.map_or(class_node.start_position().row, |node| {
17930            node.start_position().row
17931        }) + 1,
17932        end_line: semicolon.end_position().row + 1,
17933    };
17934    Some(CppSentinelFragmentedClassTail {
17935        class_node: *class_node,
17936        template_node: *template_node,
17937        name: name.clone(),
17938        raw_supertypes: raw_supertypes.clone(),
17939        fragmented: FragmentedExportBody {
17940            reparse_start: *reparse_start,
17941            reparse_end,
17942            class_range,
17943        },
17944        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
17945        member_container: *member_container,
17946    })
17947}
17948
17949/// The alias declarations a fragmented sentinel class still holds as
17950/// structured direct members in the original tree (#6902).
17951///
17952/// The member reparse cannot keep them: after an access label it reads
17953/// `using value_type = int;` in statement context, as a `using` variable
17954/// named `value_type`, and loses the target. The original node keeps the
17955/// alias kind, the target type, the exact source range, and the template and
17956/// preprocessor context. Ownership is proven by position alone: a direct
17957/// child of the class's member container, inside the interior the member
17958/// reparse covers, or inside a preprocessor conditional that is one. An alias
17959/// nested anywhere else is not admitted here.
17960fn cpp_sentinel_fragmented_class_aliases<'tree>(
17961    tail: &CppSentinelFragmentedClassTail<'tree>,
17962) -> Vec<Node<'tree>> {
17963    let mut aliases = Vec::new();
17964    let mut containers = vec![tail.member_container];
17965    while let Some(container) = containers.pop() {
17966        let mut cursor = container.walk();
17967        for child in container.named_children(&mut cursor) {
17968            if child.start_byte() < tail.fragmented.reparse_start
17969                || child.end_byte() > tail.fragmented.reparse_end
17970            {
17971                continue;
17972            }
17973            match child.kind() {
17974                // A conditional member keeps its owner; its guard comes from
17975                // this same original ancestry.
17976                "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
17977                | "preproc_elifdef" => containers.push(child),
17978                "alias_declaration" if !child.has_error() => aliases.push(child),
17979                "template_declaration"
17980                    if !child.has_error()
17981                        && child
17982                            .named_children(&mut child.walk())
17983                            .any(|declaration| declaration.kind() == "alias_declaration") =>
17984                {
17985                    aliases.push(child)
17986                }
17987                _ => {}
17988            }
17989        }
17990    }
17991    aliases
17992}
17993
17994/// Recover the class and out-of-line owner scopes from every malformed
17995/// namespace-sentinel region in `root`.
17996///
17997/// This is the shared structural counterpart to
17998/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
17999/// reuses the visitor's sentinel/class admission predicates instead of parsing
18000/// source text a second time.  The returned values own only ranges and names, so
18001/// they can be retained by an inverted usage scan after the tree borrow ends.
18002pub fn cpp_sentinel_recovered_classes(
18003    root: Node<'_>,
18004    source: &str,
18005) -> Vec<CppSentinelRecoveredClass> {
18006    // The macro-declaration grammar absorbs the sentinel damage into structured
18007    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
18008    // admitted only where the older grammar would have reported an error, so a
18009    // clean tree carrying them holds the same recovery evidence. A tree with
18010    // neither errors nor macro-declaration nodes has no sentinel region.
18011    if !root.has_error()
18012        && !subtree_contains(root, |node| {
18013            matches!(
18014                node.kind(),
18015                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18016            )
18017        })
18018    {
18019        return Vec::new();
18020    }
18021    // This scan owns its walk of `root`, so it owns the parent index that walk
18022    // asks its ancestor questions through. Built after the error gate: a clean
18023    // tree without macro-declaration evidence returns without paying for one.
18024    let ancestry = ParentIndex::new(root);
18025    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18026    let mut stack = vec![root];
18027    while let Some(current) = stack.pop() {
18028        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18029            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18030        {
18031            let namespace_components = cpp_sentinel_recovered_namespace_components(
18032                recovered.function,
18033                &recovered.namespace_components,
18034                source,
18035            );
18036            let fragmented = cpp_sentinel_fragmented_class_tail(
18037                recovered.function,
18038                recovered.body,
18039                source,
18040                &ancestry,
18041            );
18042            let mut class_candidates = Vec::new();
18043            let mut cursor = recovered.body.walk();
18044            for (class_node, template_node) in recovered
18045                .body
18046                .named_children(&mut cursor)
18047                .filter_map(cpp_sentinel_body_class_candidate)
18048            {
18049                let Some(name) = class_like_name(class_node, source, &ancestry) else {
18050                    continue;
18051                };
18052                if name.is_empty() {
18053                    continue;
18054                }
18055                let is_fragmented = fragmented
18056                    .as_ref()
18057                    .is_some_and(|tail| same_node(tail.class_node, class_node));
18058                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18059                    continue;
18060                }
18061                let class_range = if is_fragmented {
18062                    fragmented
18063                        .as_ref()
18064                        .map(|tail| tail.fragmented.class_range)
18065                        .expect("fragmented class range is present when class matches")
18066                } else {
18067                    cpp_declaration_range(template_node.unwrap_or(class_node))
18068                };
18069                class_candidates.push((class_range, name));
18070            }
18071            if let Some(fragmented) = fragmented
18072                .as_ref()
18073                .filter(|tail| tail.class_node.kind() == "ERROR")
18074            {
18075                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18076            }
18077
18078            let mut owner_ranges =
18079                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18080            cpp_sentinel_extend_unique_owner_ranges(
18081                &mut owner_ranges,
18082                cpp_sentinel_recovered_sibling_owner_ranges(
18083                    recovered.function,
18084                    &namespace_components,
18085                    source,
18086                ),
18087            );
18088            for (class_range, name) in class_candidates {
18089                push_cpp_sentinel_recovered_class(
18090                    &mut recovered_classes,
18091                    cpp_declaration_range(recovered.body),
18092                    &namespace_components,
18093                    class_range,
18094                    name,
18095                    &owner_ranges,
18096                );
18097            }
18098
18099            if let Some(declaration_list) = recovered
18100                .function
18101                .parent()
18102                .filter(|parent| parent.kind() == "declaration_list")
18103            {
18104                let outer_namespace =
18105                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18106                push_cpp_sentinel_sibling_classes(
18107                    &mut recovered_classes,
18108                    declaration_list,
18109                    recovered.function,
18110                    &outer_namespace,
18111                    source,
18112                    &ancestry,
18113                );
18114            }
18115        } else if let Some(region) =
18116            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18117        {
18118            let namespace_components = cpp_sentinel_recovered_namespace_components(
18119                current,
18120                &region.namespace_components,
18121                source,
18122            );
18123            let owner_container = current
18124                .parent()
18125                .filter(|parent| parent.kind() == "declaration_list")
18126                .unwrap_or(current);
18127            let owner_ranges =
18128                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18129            push_cpp_sentinel_recovered_class(
18130                &mut recovered_classes,
18131                cpp_declaration_range(owner_container),
18132                &namespace_components,
18133                Range {
18134                    start_byte: region.class_start,
18135                    end_byte: region.class_close_end,
18136                    start_line: region.class_start_line,
18137                    end_line: region.class_close_line,
18138                },
18139                region.name,
18140                &owner_ranges,
18141            );
18142        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18143            // A generic sentinel-prefixed class can be reduced as a malformed
18144            // function/ERROR without the explicit `namespace X` token pair.
18145            // Reuse the declaration visitor's bounded reparse and retain only
18146            // the recovered class identity/range here.
18147            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18148                region;
18149            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18150                continue;
18151            };
18152            let root = tree.root_node();
18153            let template_node = cpp_sentinel_reparsed_leading_template(root);
18154            // A region reparse is its own tree and needs its own parent index.
18155            let reparsed_ancestry = ParentIndex::new(root);
18156            let Some(reparsed_class) =
18157                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18158            else {
18159                continue;
18160            };
18161            let class_node = reparsed_class.declaration_node;
18162            let name = reparsed_class.name;
18163            let namespace_components =
18164                cpp_sentinel_recovered_namespace_components(current, &[], source);
18165            let owner_container = current
18166                .parent()
18167                .filter(|parent| parent.kind() == "declaration_list")
18168                .unwrap_or(current);
18169            let mut owner_ranges =
18170                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18171            cpp_sentinel_extend_unique_owner_ranges(
18172                &mut owner_ranges,
18173                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18174            );
18175            push_cpp_sentinel_recovered_class(
18176                &mut recovered_classes,
18177                cpp_declaration_range(owner_container),
18178                &namespace_components,
18179                Range {
18180                    start_byte: class_start,
18181                    end_byte: close_end,
18182                    start_line: class_node.start_position().row + 1,
18183                    end_line: class_node.end_position().row + 1,
18184                },
18185                name,
18186                &owner_ranges,
18187            );
18188            if owner_container.kind() == "declaration_list" {
18189                push_cpp_sentinel_sibling_classes(
18190                    &mut recovered_classes,
18191                    owner_container,
18192                    current,
18193                    &namespace_components,
18194                    source,
18195                    &ancestry,
18196                );
18197            }
18198        }
18199
18200        let mut cursor = current.walk();
18201        stack.extend(current.named_children(&mut cursor));
18202    }
18203    // A shallower sentinel can expose nested classes as apparent namespace
18204    // siblings even after a deeper sentinel proves that a containing class
18205    // owns their ranges. Drop those shadow descriptors; scope recovery starts
18206    // from the proven containing class and appends parser-visible class
18207    // ancestors, preserving the full `Outer::Inner` chain.
18208    let shadowed = recovered_classes
18209        .iter()
18210        .map(|candidate| {
18211            recovered_classes.iter().any(|container| {
18212                container.class_range.start_byte <= candidate.class_range.start_byte
18213                    && container.class_range.end_byte >= candidate.class_range.end_byte
18214                    && container.class_range != candidate.class_range
18215                    && container.namespace_scope_components.len()
18216                        > candidate.namespace_scope_components.len()
18217                    && container
18218                        .namespace_scope_components
18219                        .starts_with(&candidate.namespace_scope_components)
18220            })
18221        })
18222        .collect::<Vec<_>>();
18223    let mut index = 0usize;
18224    recovered_classes.retain(|_| {
18225        let keep = !shadowed[index];
18226        index += 1;
18227        keep
18228    });
18229    recovered_classes
18230}
18231
18232/// A flat sentinel can swallow the first class while leaving later classes and
18233/// their out-of-line definitions as ordinary declaration-list siblings.  Once
18234/// the malformed class proves the sentinel envelope, retain those structurally
18235/// complete sibling classes under the same surviving namespace so every member
18236/// owner in the region uses one recovery contract.
18237fn push_cpp_sentinel_sibling_classes<'tree>(
18238    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18239    declaration_list: Node<'tree>,
18240    sentinel_node: Node<'tree>,
18241    namespace_components: &[String],
18242    source: &str,
18243    ancestry: &ParentIndex<'tree>,
18244) {
18245    let owner_ranges =
18246        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18247    let namespace_range = cpp_declaration_range(declaration_list);
18248    let mut cursor = declaration_list.walk();
18249    for (class_node, template_node) in declaration_list
18250        .named_children(&mut cursor)
18251        .filter(|child| !same_node(*child, sentinel_node))
18252        .filter_map(cpp_sentinel_body_class_candidate)
18253    {
18254        let Some(name) = class_like_name(class_node, source, ancestry) else {
18255            continue;
18256        };
18257        if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18258            continue;
18259        }
18260        push_cpp_sentinel_recovered_class(
18261            recovered_classes,
18262            namespace_range,
18263            namespace_components,
18264            cpp_declaration_range(template_node.unwrap_or(class_node)),
18265            name,
18266            &owner_ranges,
18267        );
18268    }
18269}
18270
18271fn push_cpp_sentinel_recovered_class(
18272    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18273    namespace_range: Range,
18274    namespace_components: &[String],
18275    class_range: Range,
18276    name: String,
18277    owner_ranges: &[CppSentinelRecoveredOwner],
18278) {
18279    let mut scope_components = namespace_components.to_vec();
18280    scope_components.push(name);
18281    let owner_ranges = owner_ranges
18282        .iter()
18283        .filter(|owner| owner.scope_components.starts_with(&scope_components))
18284        .cloned()
18285        .collect::<Vec<_>>();
18286    if recovered_classes.iter().any(|existing| {
18287        existing.class_range == class_range && existing.scope_components == scope_components
18288    }) {
18289        return;
18290    }
18291    recovered_classes.push(CppSentinelRecoveredClass {
18292        namespace_range,
18293        namespace_scope_components: namespace_components.to_vec(),
18294        class_range,
18295        scope_components,
18296        owner_ranges,
18297    });
18298}
18299
18300fn cpp_sentinel_recovered_namespace_components(
18301    function: Node<'_>,
18302    recovered_components: &[String],
18303    source: &str,
18304) -> Vec<String> {
18305    let mut ancestor_components = Vec::new();
18306    let mut ancestor = function.parent();
18307    while let Some(current) = ancestor {
18308        if current.kind() == "namespace_definition"
18309            && let Some(name_node) = current.child_by_field_name("name")
18310            && let Some(components) = cpp_name_components(name_node, source)
18311        {
18312            ancestor_components.push(
18313                components
18314                    .into_iter()
18315                    .map(|component| component.name)
18316                    .collect::<Vec<_>>(),
18317            );
18318        }
18319        ancestor = current.parent();
18320    }
18321    ancestor_components.reverse();
18322    let mut ancestors = ancestor_components
18323        .into_iter()
18324        .flatten()
18325        .collect::<Vec<_>>();
18326
18327    let overlap = (0..=ancestors.len().min(recovered_components.len()))
18328        .rev()
18329        .find(|length| {
18330            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18331        })
18332        .unwrap_or(0);
18333    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18334    ancestors
18335}
18336
18337fn cpp_sentinel_recovered_owner_ranges(
18338    body: Node<'_>,
18339    namespace_components: &[String],
18340    source: &str,
18341) -> Vec<CppSentinelRecoveredOwner> {
18342    let mut owners = Vec::new();
18343    walk_named_tree_preorder(body, true, |node| {
18344        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18345    });
18346    owners
18347}
18348
18349fn cpp_sentinel_collect_owner_range(
18350    node: Node<'_>,
18351    namespace_components: &[String],
18352    source: &str,
18353    owners: &mut Vec<CppSentinelRecoveredOwner>,
18354) -> WalkControl {
18355    if node.kind() != "function_definition" {
18356        return WalkControl::Continue;
18357    }
18358    let Some(function_declarator) = extract_function_declarator(node) else {
18359        return WalkControl::Continue;
18360    };
18361    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18362        return WalkControl::Continue;
18363    };
18364    let Some(mut components) = cpp_name_components(name_node, source) else {
18365        return WalkControl::Continue;
18366    };
18367    if components.len() <= 1 {
18368        return WalkControl::Continue;
18369    }
18370    components.pop();
18371    let mut owner_components = components
18372        .into_iter()
18373        .map(|component| component.name)
18374        .collect::<Vec<_>>();
18375    let overlap = (0..=namespace_components.len().min(owner_components.len()))
18376        .rev()
18377        .find(|length| {
18378            owner_components[..*length]
18379                == namespace_components[namespace_components.len().saturating_sub(*length)..]
18380        })
18381        .unwrap_or(0);
18382    let mut scope_components = namespace_components.to_vec();
18383    scope_components.extend(owner_components.drain(overlap..));
18384    if scope_components.len() <= namespace_components.len() {
18385        return WalkControl::Continue;
18386    }
18387    let range = cpp_declaration_range(node);
18388    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18389        existing.range == range && existing.scope_components == scope_components
18390    }) {
18391        owners.push(CppSentinelRecoveredOwner {
18392            range,
18393            owner_name_start_byte: name_node.start_byte(),
18394            namespace_component_count: namespace_components.len(),
18395            scope_components,
18396        });
18397    }
18398    WalkControl::Continue
18399}
18400
18401fn cpp_sentinel_extend_unique_owner_ranges(
18402    owners: &mut Vec<CppSentinelRecoveredOwner>,
18403    additional: Vec<CppSentinelRecoveredOwner>,
18404) {
18405    for owner in additional {
18406        if !owners.iter().any(|existing| {
18407            existing.range == owner.range && existing.scope_components == owner.scope_components
18408        }) {
18409            owners.push(owner);
18410        }
18411    }
18412}
18413
18414fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18415    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18416        return false;
18417    }
18418    let Some(end_name) = node.named_child(0) else {
18419        return false;
18420    };
18421    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18422        return false;
18423    }
18424    let mut cursor = node.walk();
18425    node.children(&mut cursor)
18426        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18427}
18428
18429/// Collect owner definitions that the malformed sentinel left as later
18430/// declaration-list siblings. Parser-visible namespace siblings are a hard
18431/// boundary: their declarations must keep their own lexical namespace.
18432fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18433    parent: Node<'_>,
18434    sentinel_node: Node<'_>,
18435    namespace_components: &[String],
18436    source: &str,
18437) -> Vec<CppSentinelRecoveredOwner> {
18438    let mut owners = Vec::new();
18439    let mut after_sentinel = false;
18440    let mut cursor = parent.walk();
18441    for child in parent.named_children(&mut cursor) {
18442        if !after_sentinel {
18443            if same_node(child, sentinel_node) {
18444                after_sentinel = true;
18445            }
18446            continue;
18447        }
18448        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18449    }
18450    owners
18451}
18452
18453/// Collect owner definitions after a malformed namespace, stopping only at
18454/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18455/// enclosing container is not trusted to belong to the recovered namespace.
18456fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18457    parent: Node<'_>,
18458    sentinel_node: Node<'_>,
18459    namespace_components: &[String],
18460    source: &str,
18461) -> Option<Vec<CppSentinelRecoveredOwner>> {
18462    let mut owners = Vec::new();
18463    let mut after_namespace = false;
18464    let mut cursor = parent.walk();
18465    for child in parent.named_children(&mut cursor) {
18466        if !after_namespace {
18467            if same_node(child, sentinel_node) {
18468                after_namespace = true;
18469            }
18470            continue;
18471        }
18472        if cpp_sentinel_namespace_end(child, source) {
18473            return Some(owners);
18474        }
18475        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18476    }
18477    None
18478}
18479
18480fn cpp_sentinel_append_owner_ranges(
18481    sibling: Node<'_>,
18482    namespace_components: &[String],
18483    source: &str,
18484    owners: &mut Vec<CppSentinelRecoveredOwner>,
18485) {
18486    walk_named_tree_preorder(sibling, true, |node| {
18487        if node.kind() == "namespace_definition" {
18488            return WalkControl::SkipChildren;
18489        }
18490        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18491    });
18492}
18493
18494fn cpp_sentinel_recovered_sibling_owner_ranges(
18495    sentinel_node: Node<'_>,
18496    namespace_components: &[String],
18497    source: &str,
18498) -> Vec<CppSentinelRecoveredOwner> {
18499    let Some(declaration_list) = sentinel_node
18500        .parent()
18501        .filter(|parent| parent.kind() == "declaration_list")
18502    else {
18503        return Vec::new();
18504    };
18505    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18506        declaration_list,
18507        sentinel_node,
18508        namespace_components,
18509        source,
18510    );
18511
18512    let Some(namespace) = declaration_list
18513        .parent()
18514        .filter(|parent| parent.kind() == "namespace_definition")
18515    else {
18516        return owners;
18517    };
18518    let Some(outer_parent) = namespace.parent() else {
18519        return owners;
18520    };
18521    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18522        outer_parent,
18523        namespace,
18524        namespace_components,
18525        source,
18526    ) {
18527        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18528    }
18529    owners
18530}
18531
18532fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18533    let mut current = function_declarator.child_by_field_name("declarator")?;
18534    loop {
18535        if let Some(name) = macro_decorated_unqualified_name(current) {
18536            current = name;
18537            continue;
18538        }
18539        if matches!(
18540            current.kind(),
18541            "qualified_identifier"
18542                | "scoped_identifier"
18543                | "scoped_type_identifier"
18544                | "identifier"
18545                | "field_identifier"
18546                | "operator_name"
18547                | "destructor_name"
18548                | "literal_operator_name"
18549        ) {
18550            return Some(current);
18551        }
18552        current = current
18553            .child_by_field_name("declarator")
18554            .or_else(|| current.child_by_field_name("name"))
18555            .or_else(|| last_named_child(current))?;
18556    }
18557}
18558
18559/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18560/// `::` between the halves. The scope is an attribute-like macro token, not
18561/// a namespace; `Name` is the declared name.
18562///
18563/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18564/// tree-sitter joins the attribute macro and the constructor name into one
18565/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18566/// That flag is the grammar's own record that no separator is in the source,
18567/// so read it instead of looking for `::` in the node text. A genuine
18568/// `Outer::Inner` keeps a present separator and is declined here, and so is
18569/// the explicit-global `::name`, whose `::` is present and whose scope is
18570/// absent.
18571fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
18572    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
18573        return None;
18574    }
18575    let mut cursor = node.walk();
18576    if node
18577        .children(&mut cursor)
18578        .any(|child| child.kind() == "::" && !child.is_missing())
18579    {
18580        return None;
18581    }
18582    node.child_by_field_name("name")
18583}
18584
18585fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
18586    enum Component<'tree> {
18587        Node(Node<'tree>),
18588        Name(Node<'tree>),
18589    }
18590    let mut stack = vec![Component::Node(node)];
18591    let mut components = Vec::new();
18592    while let Some(component) = stack.pop() {
18593        let node = match component {
18594            Component::Name(node) => {
18595                components.push(canonical_cpp_qualified_component(
18596                    node.child_by_field_name("name")?,
18597                    source,
18598                )?);
18599                continue;
18600            }
18601            Component::Node(node) => node,
18602        };
18603        if let Some(name) = macro_decorated_unqualified_name(node) {
18604            stack.push(Component::Node(name));
18605            continue;
18606        }
18607        match node.kind() {
18608            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
18609                stack.push(Component::Name(node));
18610                if let Some(scope) = node.child_by_field_name("scope") {
18611                    stack.push(Component::Node(scope));
18612                }
18613            }
18614            _ => components.push(canonical_cpp_qualified_component(node, source)?),
18615        }
18616    }
18617    Some(components)
18618}
18619
18620fn cpp_sentinel_fragment_boundary<'tree>(
18621    function: Node<'tree>,
18622    class_node: Node<'tree>,
18623    class_body: Node<'tree>,
18624    source: &str,
18625) -> Option<(Node<'tree>, Node<'tree>)> {
18626    let declaration_list = function.parent()?;
18627    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
18628        return None;
18629    }
18630    let namespace = declaration_list.parent()?;
18631    if namespace.kind() != "namespace_definition"
18632        || namespace.child_by_field_name("body") != Some(declaration_list)
18633    {
18634        return None;
18635    }
18636    let mut cursor = declaration_list.walk();
18637    let closes = declaration_list
18638        .children(&mut cursor)
18639        .filter(|child| {
18640            !child.is_named()
18641                && child.kind() == "}"
18642                && child.start_byte() >= function.end_byte()
18643                && child.start_byte() > class_node.end_byte()
18644                && child.start_byte() > class_body.start_byte()
18645        })
18646        .collect::<Vec<_>>();
18647    let [close] = closes.as_slice() else {
18648        return None;
18649    };
18650    let semicolon = namespace.next_named_sibling()?;
18651    if !cpp_is_stray_semicolon(semicolon, source)
18652        || close.end_byte() != namespace.end_byte()
18653        || semicolon.start_byte() < namespace.end_byte()
18654    {
18655        return None;
18656    }
18657    Some((*close, semicolon))
18658}
18659
18660/// Detect the bogus declaration/function tree that tree-sitter recovers for a
18661/// region prefixed by an object-like macro sentinel the parser cannot see
18662/// (issue #941), and return the byte range `[start, end)` of the swallowed
18663/// declaration interior to reparse.
18664///
18665/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
18666/// `function_definition` whose first non-comment named child is the sentinel
18667/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
18668/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
18669/// the real items.
18670/// `start` is the end of the sentinel identifier -- everything after it is the
18671/// genuine source. `end` is the node's end, extended across any trailing empty
18672/// `;` statement the mis-parse displaced past the node (the class/struct closing
18673/// semicolon), so the reparse sees a complete, brace-balanced item.
18674///
18675/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
18676/// node (`has_error`), or -- under the macro-declaration grammar, which parses
18677/// the sentinel region without recovery -- carry the sentinel as a
18678/// `macro_modifier`, a production admitted only where the parse would
18679/// otherwise contain an error. Unknown annotation/export macros can make a
18680/// real callable error-recovered even though tree-sitter still preserves its
18681/// declarator, so a preserved callable is admitted only when a displaced class
18682/// keyword precedes that declarator. The clean-reparse-to-items gate in
18683/// `cpp_reparsed_items_are_indexable` is the final arbiter.
18684/// Return the reparse start and, when present, the structurally recovered class
18685/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
18686/// separately from the reparse start because an opaque template-declaration
18687/// macro may precede it.
18688fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
18689    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
18690        return None;
18691    }
18692    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
18693    // retain a valid function declarator despite the unknown export macro making
18694    // the outer node erroneous. Remember that declarator for the ordering gate
18695    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
18696    // class keyword precedes a spurious callable assembled from a later member.
18697    let mut declarator_cursor = node.walk();
18698    let preserved_callable = node
18699        .children_by_field_name("declarator", &mut declarator_cursor)
18700        .find_map(extract_function_declarator);
18701    // Leading documentation comments are attached to the malformed
18702    // `function_definition` as named children.  They are not part of the
18703    // sentinel prefix, so select the first non-comment child structurally
18704    // rather than requiring the sentinel to be child zero.  This is the shape
18705    // emitted for nlohmann/json's `basic_json`: its class documentation comment
18706    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
18707    // envelope otherwise ends at the first nested union.
18708    let mut cursor = node.walk();
18709    let first = node
18710        .named_children(&mut cursor)
18711        .find(|child| child.kind() != "comment")?;
18712    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18713    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18714    // the same token is wrapped in. The wrapper is itself the recovery
18715    // evidence the older grammar's `has_error` carried, so a clean envelope is
18716    // admitted only in that form. The macro-declaration grammar can also keep
18717    // the sentinel as the `type` and absorb the displaced head that follows it
18718    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
18719    // `macro_modifier_declarator` chain, which carries the same evidence.
18720    let (first_end, sentinel) = if first.kind() == "type_identifier" {
18721        if !node.has_error()
18722            && node
18723                .child_by_field_name("declarator")
18724                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
18725        {
18726            return None;
18727        }
18728        (
18729            first.end_byte(),
18730            normalize_cpp_whitespace(node_text(first, source)),
18731        )
18732    } else {
18733        let identifier = macro_modifier_identifier(first)?;
18734        (
18735            first.end_byte(),
18736            normalize_cpp_whitespace(node_text(identifier, source)),
18737        )
18738    };
18739    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18740        return None;
18741    }
18742    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
18743    // makes the trailing sentinel of one region and the leading sentinel of the
18744    // next both land as bare macro-token identifiers ahead of the real content.
18745    // Advance past every leading macro-token identifier so the reparse begins at
18746    // genuine source rather than another sentinel that would re-form the bogus
18747    // shape and fail the reparse gate.
18748    let mut start = first_end;
18749    let mut after_first = false;
18750    let mut cursor = node.walk();
18751    for child in node.named_children(&mut cursor) {
18752        if !after_first {
18753            if same_node(child, first) {
18754                after_first = true;
18755            }
18756            continue;
18757        }
18758        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
18759            Some(child)
18760        } else {
18761            macro_modifier_identifier(child)
18762        };
18763        if macro_token.is_some_and(|token| {
18764            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
18765        }) {
18766            start = child.end_byte();
18767        } else {
18768            break;
18769        }
18770    }
18771    // An additional opaque template-declaration macro before a class can be
18772    // folded into the bogus function's qualified declarator.  In that shape
18773    // the macro is not a direct sibling we can skip above; tree-sitter exposes
18774    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
18775    // Reparse from that keyword (or a real preceding `template` keyword) so the
18776    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
18777    // a class nested in a genuine sentinel-wrapped namespace lies after the
18778    // body opening and must not change the established region start.
18779    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
18780    let mut class_start = None;
18781    let mut template_start = None;
18782    let mut stack = vec![node];
18783    while let Some(current) = stack.pop() {
18784        if current.start_byte() >= prefix_end {
18785            continue;
18786        }
18787        if matches!(
18788            current.kind(),
18789            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
18790        ) {
18791            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
18792                "class" | "struct" | "union" | "enum" => {
18793                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
18794                        seen.min(current.start_byte())
18795                    }));
18796                }
18797                "template" => {
18798                    template_start =
18799                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
18800                            seen.min(current.start_byte())
18801                        }));
18802                }
18803                _ => {}
18804            }
18805        }
18806        let mut cursor = current.walk();
18807        stack.extend(current.children(&mut cursor));
18808    }
18809    if preserved_callable.is_some_and(|callable| {
18810        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
18811    }) {
18812        return None;
18813    }
18814    if let Some(class_start) = class_start {
18815        start = template_start
18816            .filter(|template_start| *template_start < class_start)
18817            .unwrap_or(class_start);
18818    }
18819    Some((start, class_start))
18820}
18821
18822/// Locate a sentinel-prefixed class whose malformed declaration was split across
18823/// root-level siblings. The true class close is represented structurally as a
18824/// lone `}` error followed by the class's displaced `;`; nested method/body
18825/// errors are not direct siblings of the sentinel node and therefore cannot
18826/// satisfy this pair.
18827fn cpp_sentinel_macro_class_region<'tree>(
18828    node: Node<'tree>,
18829    source: &str,
18830) -> Option<(usize, usize, usize, usize, usize, usize)> {
18831    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
18832        return None;
18833    };
18834    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
18835        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
18836        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
18837    if class_start >= body_open_start {
18838        return None;
18839    }
18840    let sibling_close = {
18841        let mut sibling = node.next_named_sibling();
18842        let mut found = None;
18843        while let Some(current) = sibling {
18844            let next = current.next_named_sibling();
18845            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
18846                found = Some((
18847                    current.start_byte(),
18848                    semicolon.end_byte(),
18849                    semicolon.end_position().row + 1,
18850                ));
18851                break;
18852            }
18853            sibling = next;
18854        }
18855        found
18856    };
18857    // A stray `};` sibling is this class's close only when the bounded reparse
18858    // agrees the first body-bearing class ENDS there. When the malformed
18859    // envelope swallowed the class's true close, the scan can promote a much
18860    // later scope's close instead -- in protobuf-generated headers
18861    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
18862    // `struct TableStruct_*` paired with the first message class's `};`, making
18863    // the recovered "class body" span whole `namespace {}` blocks and minting
18864    // namespace-scope classes as nested members of the recovered class, which
18865    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
18866    // (#2275). On disagreement, fall through to the suffix-reparse boundary
18867    // below, which derives the close from the class node's own balanced body
18868    // range.
18869    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
18870        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18871            return false;
18872        };
18873        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18874        // A region reparse is its own tree and needs its own parent index.
18875        let reparsed_ancestry = ParentIndex::new(tree.root_node());
18876        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
18877            tree.root_node(),
18878            template_node,
18879            source,
18880            &reparsed_ancestry,
18881        ) else {
18882            return false;
18883        };
18884        let body = reparsed_class.body;
18885        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
18886    });
18887    let (class_close_start, class_close_end, class_close_line) =
18888        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
18889            (class_close_start, class_close_end, class_close_line)
18890        } else {
18891            // When the malformed envelope itself is an ERROR, tree-sitter can
18892            // leave the class's balanced close in the source while promoting
18893            // all following members to siblings. Reparse the complete suffix
18894            // and use the first body-bearing class node's own field range as
18895            // the partition boundary. This keeps balancing in tree-sitter and
18896            // preserves the source's original byte offsets.
18897            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
18898            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
18899            // A region reparse is its own tree and needs its own parent index.
18900            let reparsed_ancestry = ParentIndex::new(tree.root_node());
18901            let reparsed_class = cpp_sentinel_reparsed_class(
18902                tree.root_node(),
18903                template_node,
18904                source,
18905                &reparsed_ancestry,
18906            )?;
18907            let body = reparsed_class.body;
18908            let class_close_end = body.end_byte();
18909            let class_close_start = class_close_end.checked_sub(1)?;
18910            let class_close_line = body.end_position().row + 1;
18911            (class_close_start, class_close_end, class_close_line)
18912        };
18913    if class_close_start <= class_start {
18914        return None;
18915    }
18916
18917    // Reparse only far enough to expose the class body opening. This is a
18918    // structured check that the candidate really begins with a body-bearing
18919    // class-like item; the original malformed tree cannot provide that node.
18920    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
18921    let class_root = tree.root_node();
18922    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
18923    // A region reparse is its own tree and needs its own parent index.
18924    let reparsed_ancestry = ParentIndex::new(class_root);
18925    let reparsed_class =
18926        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
18927    let body = reparsed_class.body;
18928    // The class body opening must agree with the malformed wrapper's structured
18929    // body field. This rejects an inner nested class while permitting later
18930    // members to remain fragmented as root-level siblings in the bounded parse.
18931    if body.start_byte() != body_open_start {
18932        return None;
18933    }
18934    let body_start = body.start_byte().checked_add(1)?;
18935    (body_start < class_close_start).then_some((
18936        reparse_start,
18937        class_start,
18938        body_start,
18939        class_close_start,
18940        class_close_end,
18941        class_close_line,
18942    ))
18943}
18944
18945/// Find the `{` token immediately following the class/struct/union/enum token
18946/// at `class_start` in the malformed tree. The token is anonymous in the C++
18947/// grammar, so this deliberately walks all children (not only named children)
18948/// and relies on sibling structure rather than source-text searching.
18949fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
18950    let mut stack = vec![node];
18951    while let Some(current) = stack.pop() {
18952        if current.start_byte() == class_start
18953            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
18954        {
18955            // The macro-declaration grammar keeps the class whole: the token's
18956            // specifier ancestor owns the body, and its `{` is the body's
18957            // first child rather than a later sibling of the token.
18958            let mut ancestor = current.parent();
18959            while let Some(parent) = ancestor {
18960                if matches!(
18961                    parent.kind(),
18962                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
18963                ) {
18964                    if let Some(body) = cpp_body_node(parent) {
18965                        return body
18966                            .child(0)
18967                            .filter(|open| open.kind() == "{")
18968                            .map(|open| open.start_byte());
18969                    }
18970                    break;
18971                }
18972                if matches!(parent.kind(), "ERROR" | "declaration") {
18973                    break;
18974                }
18975                ancestor = parent.parent();
18976            }
18977            let mut sibling = current.next_sibling();
18978            while let Some(candidate) = sibling {
18979                if candidate.kind() == "{" {
18980                    return Some(candidate.start_byte());
18981                }
18982                sibling = candidate.next_sibling();
18983            }
18984        }
18985        let mut cursor = current.walk();
18986        stack.extend(current.children(&mut cursor));
18987    }
18988    None
18989}
18990
18991/// The class body that tree-sitter displaced out of a sentinel-prefixed
18992/// declaration and left as the malformed node's next sibling.
18993///
18994/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
18995/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
18996/// the last child of that `ERROR` and its `{` opens a sibling
18997/// `compound_statement` instead. The body is still the malformed tree's own
18998/// structured token, which is what the caller's `body.start_byte() !=
18999/// body_open_start` agreement check needs; it just is not reachable by walking
19000/// forward from the class token inside the node.
19001fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
19002    node.next_named_sibling()
19003        .filter(|sibling| sibling.kind() == "compound_statement")
19004}
19005
19006fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19007    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19008    let mut end = if class_start.is_some() {
19009        cpp_macro_prefixed_class_end(source, start)?
19010    } else {
19011        node.end_byte()
19012    };
19013    if class_start.is_none()
19014        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19015    {
19016        end = end.max(namespace_end);
19017    }
19018    let mut sibling = node.next_named_sibling();
19019    while let Some(current) = sibling {
19020        if !cpp_is_stray_semicolon(current, source) {
19021            break;
19022        }
19023        end = current.end_byte();
19024        sibling = current.next_named_sibling();
19025    }
19026    (start < end).then_some((start, end))
19027}
19028
19029/// Extend a sentinel reparse through a following namespace that tree-sitter
19030/// flattened into the sentinel node's sibling list.
19031///
19032/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
19033/// unknown macro becomes a false function return type and consumes the first
19034/// namespace body. A second `namespace detail` then loses its enclosing node:
19035/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
19036/// attaches its declarations to the surrounding error tree. Reparse from that
19037/// structured keyword so tree-sitter, rather than a source-text brace scan,
19038/// supplies the complete namespace boundary.
19039fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19040    let mut sibling = node.next_sibling();
19041    let keyword = loop {
19042        let candidate = sibling?;
19043        sibling = candidate.next_sibling();
19044        if candidate.kind() != "comment" {
19045            break candidate;
19046        }
19047    };
19048    if keyword.kind() != "namespace" {
19049        return None;
19050    }
19051    let name = loop {
19052        let candidate = sibling?;
19053        sibling = candidate.next_sibling();
19054        if candidate.kind() != "comment" {
19055            break candidate;
19056        }
19057    };
19058    if cpp_namespace_name_components(name, source).is_empty() {
19059        return None;
19060    }
19061    let open = loop {
19062        let candidate = sibling?;
19063        sibling = candidate.next_sibling();
19064        if candidate.kind() != "comment" {
19065            break candidate;
19066        }
19067    };
19068    if open.kind() != "{" {
19069        return None;
19070    }
19071
19072    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19073    let root = tree.root_node();
19074    let mut cursor = root.walk();
19075    let namespace = root
19076        .named_children(&mut cursor)
19077        .find(|candidate| candidate.kind() != "comment")?;
19078    (namespace.kind() == "namespace_definition"
19079        && namespace.start_byte() == keyword.start_byte()
19080        && namespace.child_by_field_name("body").is_some())
19081    .then_some(namespace.end_byte())
19082}
19083
19084/// Parse the source suffix beginning at a structurally recovered class/template
19085/// keyword and return the end of its first body-bearing class item.  The parser,
19086/// rather than a brace scanner, owns nested-body balancing.  This is needed when
19087/// the original error tree truncates the class and scatters later members as
19088/// top-level siblings.
19089fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19090    let tree = cpp_reparse_region_items(source, start, source.len())?;
19091    let root = tree.root_node();
19092    let mut cursor = root.walk();
19093    for item in root.named_children(&mut cursor) {
19094        if item.end_byte() <= start || item.kind() == "comment" {
19095            continue;
19096        }
19097        let mut stack = vec![item];
19098        while let Some(current) = stack.pop() {
19099            if matches!(
19100                current.kind(),
19101                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19102            ) && cpp_body_node(current).is_some()
19103            {
19104                return Some(current.end_byte());
19105            }
19106            let mut cursor = current.walk();
19107            stack.extend(current.named_children(&mut cursor));
19108        }
19109        // The recovered prefix is required to begin with the class item.  If
19110        // the first real item is something else, fail closed rather than skip
19111        // arbitrary source looking for a later class.
19112        return None;
19113    }
19114    None
19115}
19116
19117/// An empty `;` statement: the displaced closing semicolon of a struct/class that
19118/// the sentinel mis-parse split off past the bogus function node.
19119fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19120    node.kind() == "expression_statement"
19121        && node.named_child_count() == 0
19122        && node_text(node, source).trim() == ";"
19123}
19124
19125fn cpp_stray_close_followed_by_semicolon<'tree>(
19126    close: Node<'tree>,
19127    next: Option<Node<'tree>>,
19128    source: &str,
19129) -> Option<Node<'tree>> {
19130    cpp_is_stray_close_brace(close, source)
19131        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19132        .flatten()
19133}
19134
19135/// Recover the member that follows CPython's object header macro when
19136/// tree-sitter folds both declarations into one malformed field.
19137///
19138/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
19139/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
19140/// sole child is the real field declarator. The tail may wrap that identifier
19141/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
19142/// malformed wrappers do not provide the same evidence that this is the
19143/// CPython macro boundary.
19144#[derive(Clone, Copy)]
19145pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19146    pub(crate) type_node: Node<'tree>,
19147    pub(crate) name: Node<'tree>,
19148    pub(crate) declarator: Node<'tree>,
19149}
19150
19151impl RecoveredPyObjectHeadField<'_> {
19152    pub(crate) fn pointer_depth(self) -> i32 {
19153        let mut depth = 0;
19154        let mut current = self.declarator;
19155        while current != self.name {
19156            debug_assert_eq!(current.kind(), "pointer_declarator");
19157            depth += 1;
19158            current = current
19159                .child_by_field_name("declarator")
19160                .expect("recovered PyObject field pointer has an inner declarator");
19161        }
19162        depth
19163    }
19164}
19165
19166pub(crate) fn recovered_pyobject_head_field<'tree>(
19167    node: Node<'tree>,
19168    source: &str,
19169) -> Option<RecoveredPyObjectHeadField<'tree>> {
19170    if node.kind() != "field_declaration" {
19171        return None;
19172    }
19173    let type_node = node.child_by_field_name("type")?;
19174    if type_node.kind() != "type_identifier"
19175        || node_text(type_node, source).trim() != "PyObject_HEAD"
19176    {
19177        return None;
19178    }
19179    let pseudo_declarator = node.child_by_field_name("declarator")?;
19180    let mut cursor = node.walk();
19181    let errors = node
19182        .named_children(&mut cursor)
19183        .filter(|child| child.kind() == "ERROR")
19184        .collect::<Vec<_>>();
19185    let [error] = errors.as_slice() else {
19186        return None;
19187    };
19188    if error.named_child_count() != 1 {
19189        return None;
19190    }
19191    let error_child = error.named_child(0)?;
19192    if pseudo_declarator.kind() == "field_identifier"
19193        && error.start_byte() >= pseudo_declarator.end_byte()
19194        && error_child.kind() == "identifier"
19195    {
19196        return Some(RecoveredPyObjectHeadField {
19197            type_node: pseudo_declarator,
19198            name: error_child,
19199            declarator: error_child,
19200        });
19201    }
19202    if pseudo_declarator.kind() != "pointer_declarator"
19203        || error.end_byte() > pseudo_declarator.start_byte()
19204        || error_child.kind() != "identifier"
19205    {
19206        return None;
19207    }
19208    let mut name = pseudo_declarator;
19209    while name.kind() == "pointer_declarator" {
19210        name = name.child_by_field_name("declarator")?;
19211    }
19212    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19213        type_node: error_child,
19214        name,
19215        declarator: pseudo_declarator,
19216    })
19217}
19218
19219/// Recover the real field name when a leading object-like annotation macro
19220/// displaces a qualified type into tree-sitter's bit-field recovery shape.
19221///
19222/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
19223/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
19224/// `bitfield_clause` containing an error plus an assignment.  The assignment's
19225/// left field is the only structured declaration name in that malformed tail.
19226/// A real bit-field is excluded by the all-caps macro type and required error.
19227fn recovered_macro_qualified_field_clause<'tree>(
19228    node: Node<'tree>,
19229    source: &str,
19230) -> Option<Node<'tree>> {
19231    if node.kind() != "field_declaration" {
19232        return None;
19233    }
19234    let macro_type = node.child_by_field_name("type")?;
19235    if macro_type.kind() != "type_identifier"
19236        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19237    {
19238        return None;
19239    }
19240    let mut cursor = node.walk();
19241    node.named_children(&mut cursor)
19242        .find(|child| child.kind() == "bitfield_clause")
19243}
19244
19245fn recovered_macro_qualified_field_declarators<'tree>(
19246    node: Node<'tree>,
19247    source: &str,
19248) -> Option<Vec<Node<'tree>>> {
19249    let pseudo_declarator = node.child_by_field_name("declarator")?;
19250    if pseudo_declarator.kind() != "field_identifier" {
19251        return None;
19252    }
19253    let clause = recovered_macro_qualified_field_clause(node, source)?;
19254    if !(0..clause.named_child_count()).any(|index| {
19255        clause
19256            .named_child(index)
19257            .is_some_and(|child| child.kind() == "ERROR")
19258    }) {
19259        return None;
19260    }
19261    let mut recovered = Vec::new();
19262    let mut stack = vec![clause];
19263    while let Some(current) = stack.pop() {
19264        if current.kind() == "assignment_expression"
19265            && let Some(left) = current.child_by_field_name("left")
19266            && extract_variable_name(left, source).is_some()
19267        {
19268            recovered.push(left);
19269            break;
19270        }
19271        let mut cursor = current.walk();
19272        stack.extend(current.named_children(&mut cursor));
19273    }
19274    if recovered.is_empty() {
19275        return None;
19276    }
19277    let mut cursor = node.walk();
19278    recovered.extend(
19279        node.children_by_field_name("declarator", &mut cursor)
19280            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19281    );
19282    Some(recovered)
19283}
19284
19285/// Recover a macro-qualified constructor that tree-sitter represents as one
19286/// field declaration. The constructor call remains inside the direct recovery
19287/// error, while each member initializer becomes a false function declarator.
19288/// The class owner proves the constructor name and lets the caller ignore those
19289/// initializer declarators.
19290fn recovered_macro_qualified_constructor_call<'tree>(
19291    node: Node<'tree>,
19292    class_name: &str,
19293    source: &str,
19294) -> Option<Node<'tree>> {
19295    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19296    let error = bitfield
19297        .named_child(0)
19298        .filter(|child| child.kind() == "ERROR")?;
19299    let mut stack = vec![error];
19300    while let Some(current) = stack.pop() {
19301        if current.kind() == "call_expression"
19302            && current
19303                .child_by_field_name("function")
19304                .is_some_and(|function| node_text(function, source) == class_name)
19305            && current
19306                .child_by_field_name("arguments")
19307                .is_some_and(|arguments| arguments.kind() == "argument_list")
19308        {
19309            return Some(current);
19310        }
19311        let mut cursor = current.walk();
19312        stack.extend(current.named_children(&mut cursor));
19313    }
19314    None
19315}
19316
19317/// The constructor head of a macro-decorated constructor that the
19318/// macro-declaration grammar reads as one field declaration
19319/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
19320/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
19321/// the constructor head is its first declarator (possibly inside a
19322/// `macro_modifier_declarator`), the first member initializer is a
19323/// `bitfield_clause` call, every later initializer is another function
19324/// declarator, and the body is the `default_value` initializer list. A
19325/// constructor declares no type, and no real field pairs a callable declarator
19326/// with a bitfield, so a head naming the enclosing class beside a bitfield
19327/// call is this shape. The caller publishes the head and none of the
19328/// initializer declarators.
19329fn recovered_macro_initialized_constructor_declarator<'tree>(
19330    node: Node<'tree>,
19331    class_name: &str,
19332    source: &str,
19333) -> Option<Node<'tree>> {
19334    if node.kind() != "field_declaration" {
19335        return None;
19336    }
19337    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19338    if cpp_function_declarator_name_node(constructor)
19339        .is_none_or(|name| node_text(name, source) != class_name)
19340    {
19341        return None;
19342    }
19343    let mut cursor = node.walk();
19344    let initializer = node
19345        .named_children(&mut cursor)
19346        .find(|child| child.kind() == "bitfield_clause")?
19347        .named_child(0)?;
19348    (initializer.kind() == "call_expression").then_some(constructor)
19349}
19350
19351/// Recover a macro-qualified member function declaration that tree-sitter
19352/// represents as a pseudo-field. An object-like export macro before a qualified
19353/// return type can displace the namespace and type into an ERROR/bitfield
19354/// recovery, leaving the callable as a structured `call_expression`.
19355///
19356/// The caller must route this shape before ordinary declarator classification;
19357/// otherwise the displaced namespace identifier is published as a field.
19358fn recovered_macro_qualified_function_call<'tree>(
19359    node: Node<'tree>,
19360    source: &str,
19361) -> Option<Node<'tree>> {
19362    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19363    let declarator = node.child_by_field_name("declarator")?;
19364    if declarator.kind() != "field_identifier" {
19365        return None;
19366    }
19367    let mut cursor = node.walk();
19368    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19369    if !named.iter().any(|child| {
19370        child.kind() == "storage_class_specifier"
19371            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19372    }) {
19373        return None;
19374    }
19375    let mut bitfield_cursor = bitfield.walk();
19376    let payload = bitfield
19377        .named_children(&mut bitfield_cursor)
19378        .collect::<Vec<_>>();
19379    let [displaced_error, call] = payload.as_slice() else {
19380        return None;
19381    };
19382    if displaced_error.kind() != "ERROR"
19383        || displaced_error.named_child_count() != 1
19384        || displaced_error
19385            .named_child(0)
19386            .is_none_or(|child| child.kind() != "identifier")
19387        || call.kind() != "call_expression"
19388        || call
19389            .child_by_field_name("function")
19390            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19391        || call
19392            .child_by_field_name("arguments")
19393            .is_none_or(|arguments| arguments.kind() != "argument_list")
19394    {
19395        return None;
19396    }
19397    Some(*call)
19398}
19399
19400/// The real constructor declarator of a macro-decorated constructor the
19401/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
19402/// holding the macro and the constructor head as a `macro_modifier_declarator`,
19403/// and the member initializer mis-read as the function's own declarator
19404/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
19405/// The constructor head inside the `ERROR` is what names the class; the
19406/// declarator the `function_definition` fields is the initializer, never the
19407/// member.
19408fn recovered_macro_decorated_constructor_declarator<'tree>(
19409    node: Node<'tree>,
19410    class_name: &str,
19411    source: &str,
19412) -> Option<Node<'tree>> {
19413    if node.kind() != "function_definition" {
19414        return None;
19415    }
19416    let mut cursor = node.walk();
19417    let mut heads = Vec::new();
19418    for error in node
19419        .named_children(&mut cursor)
19420        .filter(|child| child.kind() == "ERROR")
19421    {
19422        let mut stack = vec![error];
19423        while let Some(current) = stack.pop() {
19424            let is_constructor_head = match current.kind() {
19425                "macro_modifier_declarator" | "function_declarator" => {
19426                    extract_function_declarator(current).is_some_and(|declarator| {
19427                        cpp_function_declarator_name_node(declarator)
19428                            .is_some_and(|name| node_text(name, source) == class_name)
19429                    })
19430                }
19431                _ => false,
19432            };
19433            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19434                heads.push(declarator);
19435                continue;
19436            }
19437            let mut cursor = current.walk();
19438            stack.extend(current.named_children(&mut cursor));
19439        }
19440    }
19441    let [head] = heads.as_slice() else {
19442        return None;
19443    };
19444    Some(*head)
19445}
19446
19447fn recovered_macro_qualified_function_parameters(
19448    arguments: Node<'_>,
19449    source: &str,
19450) -> Option<(String, Vec<String>, [u8; 32])> {
19451    use brokk_bifrost_core::analyzer::tree_walk::{
19452        overload_discriminator, overload_parameter_tokens,
19453    };
19454    if arguments.kind() != "argument_list" {
19455        return None;
19456    }
19457    let mut cursor = arguments.walk();
19458    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19459    if named.is_empty() {
19460        return Some((
19461            "()".to_string(),
19462            Vec::new(),
19463            overload_discriminator(Vec::<String>::new()),
19464        ));
19465    }
19466    let mut types = Vec::new();
19467    let mut tokens = Vec::new();
19468    let mut labels = Vec::new();
19469    let mut index = 0;
19470    while index < named.len() {
19471        let parameter_type = named[index];
19472        let parameter_name = named.get(index + 1).copied()?;
19473        if !matches!(
19474            parameter_type.kind(),
19475            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19476        ) || parameter_name.kind() != "ERROR"
19477            || parameter_name.named_child_count() != 1
19478            || parameter_name
19479                .named_child(0)
19480                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19481        {
19482            return None;
19483        }
19484        let parameter_name = parameter_name.named_child(0)?;
19485        tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19486        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19487        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19488        index += 2;
19489    }
19490    Some((
19491        format!("({})", types.join(", ")),
19492        labels,
19493        overload_discriminator(tokens),
19494    ))
19495}
19496
19497/// Recognize the phantom field tree-sitter emits for a macro-qualified
19498/// function return type.  For example,
19499/// `static API result_type ThresholdForSmallA() { ... }` can become a
19500/// `field_declaration` (`API` as the type and `result_type` as a field name)
19501/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19502///
19503/// Keep this predicate entirely tied to the CST envelope: the type must be an
19504/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19505/// the declaration must carry a missing semicolon rather than a real one, and
19506/// the immediate named sibling must expose a function declarator.  A real
19507/// macro-decorated field with an explicit semicolon therefore remains a field.
19508pub fn recovered_macro_return_type_node<'tree>(
19509    node: Node<'tree>,
19510    source: &str,
19511) -> Option<Node<'tree>> {
19512    if node.kind() != "field_declaration" {
19513        return None;
19514    }
19515    let macro_type = node.child_by_field_name("type")?;
19516    if macro_type.kind() != "type_identifier"
19517        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19518    {
19519        return None;
19520    }
19521    let declarator = node.child_by_field_name("declarator")?;
19522    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19523        return None;
19524    }
19525    let mut has_missing_semicolon = false;
19526    let mut has_real_semicolon = false;
19527    for child in children_iter(node) {
19528        if child.kind() != ";" {
19529            continue;
19530        }
19531        if child.is_missing() {
19532            has_missing_semicolon = true;
19533        } else {
19534            has_real_semicolon = true;
19535        }
19536    }
19537    if !has_missing_semicolon || has_real_semicolon {
19538        return None;
19539    }
19540    let mut next = node.next_named_sibling();
19541    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19542        next = next.and_then(|sibling| sibling.next_named_sibling());
19543    }
19544    let next = next?;
19545    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19546        return None;
19547    }
19548    let function_declarator = next.child_by_field_name("declarator")?;
19549    extract_function_declarator(function_declarator).map(|_| declarator)
19550}
19551
19552/// The omitted-semicolon class field the macro-declaration grammar folds into
19553/// the head of the normally typed function that follows it. The older grammar
19554/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19555/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19556/// same pair as two leading `macro_modifier`s of the clean function
19557/// definition. Only an exact modifier pair ahead of a real type field and a
19558/// function declarator is a field: a lone modifier ahead of the type is the
19559/// macro-return shape (`static API result_type f() { ... }`), which carries
19560/// no field.
19561fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19562    if node.kind() != "function_definition" {
19563        return None;
19564    }
19565    let type_node = node.child_by_field_name("type")?;
19566    let declarator = node.child_by_field_name("declarator")?;
19567    extract_function_declarator(declarator)?;
19568    let mut cursor = node.walk();
19569    let head = node
19570        .named_children(&mut cursor)
19571        .take_while(|child| *child != type_node)
19572        .filter(|child| child.kind() != "comment")
19573        .collect::<Vec<_>>();
19574    let [type_modifier, name_modifier] = head.as_slice() else {
19575        return None;
19576    };
19577    macro_modifier_identifier(*type_modifier)?;
19578    macro_modifier_identifier(*name_modifier)?;
19579    Some((*type_modifier, *name_modifier))
19580}
19581
19582/// The stand-in field evidence in a clean macro-return function definition:
19583/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
19584/// `macro_modifier` head and a return type naming an active template type
19585/// parameter. The older grammar read the same head as a phantom
19586/// `field_declaration` named after the type, and dependent calls like
19587/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
19588/// first token and the type node; the active-parameter gate is the same one
19589/// the phantom-field suppression used, so a macro return type naming no
19590/// template parameter (`result_type`) recovers nothing.
19591fn macro_return_template_parameter_field<'tree>(
19592    node: Node<'tree>,
19593    source: &str,
19594    ancestry: &ParentIndex<'tree>,
19595) -> Option<(Node<'tree>, Node<'tree>)> {
19596    if node.kind() != "function_definition" {
19597        return None;
19598    }
19599    let type_node = node.child_by_field_name("type")?;
19600    if type_node.kind() != "type_identifier" {
19601        return None;
19602    }
19603    let declarator = node.child_by_field_name("declarator")?;
19604    extract_function_declarator(declarator)?;
19605    let mut cursor = node.walk();
19606    let head = node
19607        .named_children(&mut cursor)
19608        .take_while(|child| *child != type_node)
19609        .filter(|child| child.kind() != "comment")
19610        .collect::<Vec<_>>();
19611    let [first, ..] = head.as_slice() else {
19612        return None;
19613    };
19614    if !head
19615        .iter()
19616        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
19617        || !head.iter().any(|child| child.kind() == "macro_modifier")
19618    {
19619        return None;
19620    }
19621    let name = normalize_cpp_whitespace(node_text(type_node, source));
19622    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
19623        return None;
19624    }
19625    Some((*first, type_node))
19626}
19627
19628/// Whether `name` is a type parameter of a template declaration that lexically
19629/// encloses `node`. The malformed macro-return field uses the parameter name as
19630/// its pseudo-declarator; preserving that field is necessary to publish a
19631/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
19632/// ancestors instead of interpreting source text so nested templates and
19633/// parser-recovered regions retain their real lexical scopes.
19634pub(crate) fn cpp_active_template_type_parameter<'tree>(
19635    node: Node<'tree>,
19636    name: &str,
19637    source: &str,
19638    ancestry: &ParentIndex<'tree>,
19639) -> bool {
19640    let mut ancestor = ancestry.parent(node);
19641    while let Some(current) = ancestor {
19642        if current.kind() == "template_declaration"
19643            && let Some(parameters) = current.child_by_field_name("parameters")
19644            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
19645                cpp_template_parameter_name(parameter, source)
19646            })
19647            .type_parameter
19648        {
19649            return true;
19650        }
19651        ancestor = ancestry.parent(current);
19652    }
19653    false
19654}
19655
19656/// Reparse the region `[start, end)` of `source` as C++, confined to the region
19657/// via included ranges so every reparsed node keeps its original byte offset and
19658/// line number. The existing visitors read node text from the original source,
19659/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
19660/// `parse_rust_region_tree` technique.
19661fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
19662    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
19663}
19664
19665fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
19666    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
19667        return None;
19668    }
19669    let semicolon = node.next_sibling()?;
19670    if semicolon.kind() != ";" || semicolon.is_missing() {
19671        return None;
19672    }
19673    let row = node.start_position().row;
19674    let mut start = node.start_byte();
19675    let mut sibling = node.prev_sibling();
19676    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
19677        if previous.kind() == ";" {
19678            break;
19679        }
19680        start = previous.start_byte();
19681        sibling = previous.prev_sibling();
19682    }
19683    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
19684}
19685
19686/// One `RET name _(( args ));`-style K&R prototype-macro invocation
19687/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
19688/// byte spans a clean reparse needs, in original-source order.
19689struct PrototypeMacroCandidate {
19690    /// Where the return type and declared name begin.
19691    run_start: usize,
19692    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
19693    /// exclusive end of the first reparse span.
19694    identifier_start: usize,
19695    /// The inner `(`'s start byte: the start of the second reparse span.
19696    /// This paren is kept so the reparse sees exactly one parameter-list
19697    /// paren pair.
19698    inner_open_start: usize,
19699    /// Byte just past the inner `)`: the end of the second reparse span.
19700    inner_close_end: usize,
19701    /// Byte just past the outer `)`: the start of the third reparse span.
19702    outer_close_end: usize,
19703    /// Byte just past the terminating `;`: the end of the third reparse
19704    /// span, and of the whole candidate.
19705    semicolon_end: usize,
19706}
19707
19708impl PrototypeMacroCandidate {
19709    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
19710    /// one included-range tree: the return type and name, the parenthesized
19711    /// argument list, and the terminating `;`. The macro token and its outer
19712    /// wrapping parenthesis are omitted -- deleting them is exactly what the
19713    /// (absent) preprocessor macro expansion would do.
19714    fn ranges(&self) -> [(usize, usize); 3] {
19715        [
19716            (self.run_start, self.identifier_start),
19717            (self.inner_open_start, self.inner_close_end),
19718            (self.outer_close_end, self.semicolon_end),
19719        ]
19720    }
19721}
19722
19723/// The class a class-body prototype-macro reparse keeps around the member
19724/// (#6902): the class head through its `{`, the access label the member is
19725/// written under, and the class's `}`, at their original offsets. In that
19726/// parse the recovered prototype is a real member of a real class body, so its
19727/// `static` and its place on the body's access ladder state its modifiers
19728/// exactly as an ordinary member's do.
19729struct PrototypeMacroClassEnvelope<'tree> {
19730    /// The class's enclosing node, which is the recovery's origin.
19731    class_parent: Node<'tree>,
19732    /// The class head through the body's `{`.
19733    head: (usize, usize),
19734    /// The body's `{`, which finds the reparsed body.
19735    open: (usize, usize),
19736    /// The last `access_specifier` the body writes before the member, through
19737    /// its `:`.
19738    label: Option<(usize, usize)>,
19739    /// The body's `}`.
19740    close: (usize, usize),
19741}
19742
19743/// The [`PrototypeMacroClassEnvelope`] of one class-body member, read forward
19744/// with one cursor as [`cpp_member_declared_visibility`] reads the access
19745/// ladder. A member nested in anything but preprocessor conditionals, or under
19746/// a label a macro invocation swallowed, has no envelope that reproduces its
19747/// position, so it stays unrecovered rather than taking another access.
19748fn cpp_prototype_macro_class_envelope<'tree>(
19749    member: Node<'tree>,
19750    source: &str,
19751    ancestry: &ParentIndex<'tree>,
19752) -> Option<PrototypeMacroClassEnvelope<'tree>> {
19753    let mut list_level = member;
19754    let body = loop {
19755        let parent = ancestry.parent(list_level)?;
19756        if parent.kind() == "field_declaration_list" {
19757            break parent;
19758        }
19759        if !is_cpp_preprocessor_conditional(parent) {
19760            return None;
19761        }
19762        list_level = parent;
19763    };
19764    let class_like = ancestry.parent(body)?;
19765    let class_parent = ancestry.parent(class_like)?;
19766    let open = body.child(0).filter(|open| open.kind() == "{")?;
19767    let close = body
19768        .child(body.child_count().checked_sub(1)?)
19769        .filter(|close| close.kind() == "}" && !close.is_missing())?;
19770    let mut label = None;
19771    let mut under_swallowed_label = false;
19772    let mut cursor = body.walk();
19773    for child in body.children(&mut cursor) {
19774        if child == list_level {
19775            break;
19776        }
19777        if child.kind() == "access_specifier" {
19778            // The grammar leaves the label's `:` to the body.
19779            let colon = child
19780                .next_sibling()
19781                .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
19782            label = Some((child.start_byte(), colon.end_byte()));
19783            under_swallowed_label = false;
19784        } else if cpp_macro_swallowed_access_label(child, source).is_some() {
19785            under_swallowed_label = true;
19786        }
19787    }
19788    if under_swallowed_label {
19789        return None;
19790    }
19791    Some(PrototypeMacroClassEnvelope {
19792        class_parent,
19793        head: (class_like.start_byte(), open.end_byte()),
19794        open: (open.start_byte(), open.end_byte()),
19795        label,
19796        close: (close.start_byte(), close.end_byte()),
19797    })
19798}
19799
19800/// A live terminating semicolon owned directly by `node`.
19801fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
19802    node.child(node.child_count().checked_sub(1)?)
19803        .filter(|child| child.kind() == ";" && !child.is_missing())
19804}
19805
19806/// The known pre-ANSI prototype macros whose expansion is exactly their one
19807/// argument. The malformed CST cannot prove that an arbitrary identifier has
19808/// that definition, so unknown spellings fail closed rather than turning an
19809/// ordinary broken declaration into a Function (issue #2932).
19810fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
19811    matches!(
19812        node.kind(),
19813        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
19814    ) && matches!(
19815        normalize_cpp_whitespace(node_text(node, source)).as_str(),
19816        "_" | "__P" | "OF" | "PROTO"
19817    )
19818}
19819
19820/// Read tree-sitter's recovery for `declared_name MACRO`: a
19821/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
19822/// declared name, and whose `name` is a known prototype macro.
19823fn cpp_prototype_macro_qualified_parts<'tree>(
19824    node: Node<'tree>,
19825    source: &str,
19826) -> Option<(Node<'tree>, Node<'tree>)> {
19827    if node.kind() != "qualified_identifier" {
19828        return None;
19829    }
19830    let declared_name = node
19831        .child_by_field_name("scope")
19832        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
19833    let macro_name = macro_decorated_unqualified_name(node)?;
19834    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
19835}
19836
19837/// The inner parenthesized node of tree-sitter's structured
19838/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
19839/// the macro invocation's outer parentheses while retaining the one pair an
19840/// ordinary function declarator needs. No delimiter search is involved: both
19841/// pairs and their ownership come from the CST.
19842fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
19843    if arguments.kind() != "argument_list"
19844        || arguments.named_child_count() != 1
19845        || arguments.child_count() != 3
19846        || arguments
19847            .child(0)
19848            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19849        || arguments
19850            .child(2)
19851            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19852    {
19853        return None;
19854    }
19855    let inner = arguments.named_child(0)?;
19856    let close_index = match inner.kind() {
19857        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
19858        // tree-sitter completes the C++ cast production with a zero-width
19859        // value after the live close parenthesis.
19860        "cast_expression" => inner.child_count().checked_sub(2)?,
19861        _ => return None,
19862    };
19863    (inner
19864        .child(0)
19865        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
19866        && inner
19867            .child(close_index)
19868            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
19869    .then_some(inner)
19870}
19871
19872fn cpp_prototype_macro_candidate_from_init_declaration(
19873    declaration: Node<'_>,
19874    source: &str,
19875) -> Option<PrototypeMacroCandidate> {
19876    let init = declaration
19877        .child_by_field_name("declarator")
19878        .filter(|declarator| declarator.kind() == "init_declarator")?;
19879    let malformed_declarator = init.child_by_field_name("declarator")?;
19880    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
19881        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
19882    } else {
19883        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
19884            return None;
19885        }
19886        let declared_name_error = init
19887            .prev_named_sibling()
19888            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
19889        let declared_name = declared_name_error
19890            .named_child(0)
19891            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
19892        (declared_name, malformed_declarator)
19893    };
19894    let arguments = init
19895        .child_by_field_name("value")
19896        .filter(|value| value.kind() == "argument_list")?;
19897    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
19898    let semicolon = cpp_direct_semicolon(declaration)?;
19899    let return_type = declaration.child_by_field_name("type")?;
19900    if return_type.end_byte() > declared_name.start_byte()
19901        || declared_name.end_byte() > macro_name.start_byte()
19902        || macro_name.end_byte() > arguments.start_byte()
19903        || arguments.end_byte() > semicolon.start_byte()
19904    {
19905        return None;
19906    }
19907    Some(cpp_prototype_macro_candidate_from_parts(
19908        declaration.start_byte(),
19909        macro_name,
19910        inner,
19911        arguments,
19912        semicolon,
19913    ))
19914}
19915
19916fn cpp_prototype_macro_candidate_from_qualified_declaration(
19917    declaration: Node<'_>,
19918    source: &str,
19919) -> Option<PrototypeMacroCandidate> {
19920    let qualified = declaration
19921        .child_by_field_name("declarator")
19922        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
19923    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
19924    let open_error = qualified
19925        .next_named_sibling()
19926        .filter(|next| next.kind() == "ERROR")?;
19927    let close_error = last_named_child(declaration)
19928        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
19929    if open_error.child_count() < 3
19930        || open_error
19931            .child(0)
19932            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19933        || open_error
19934            .child(1)
19935            .is_none_or(|open| open.kind() != "(" || open.is_missing())
19936        || close_error.child_count() != 2
19937        || close_error
19938            .child(0)
19939            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19940        || close_error
19941            .child(1)
19942            .is_none_or(|close| close.kind() != ")" || close.is_missing())
19943    {
19944        return None;
19945    }
19946    let inner_open = open_error.child(1)?;
19947    let inner_close = close_error.child(0)?;
19948    let outer_close = close_error.child(1)?;
19949    let semicolon = cpp_direct_semicolon(declaration)?;
19950    let return_type = declaration.child_by_field_name("type")?;
19951    if return_type.end_byte() > qualified.start_byte()
19952        || macro_name.end_byte() > open_error.start_byte()
19953        || inner_open.start_byte() > inner_close.end_byte()
19954        || inner_close.end_byte() > outer_close.start_byte()
19955        || outer_close.end_byte() > semicolon.start_byte()
19956    {
19957        return None;
19958    }
19959    Some(PrototypeMacroCandidate {
19960        run_start: declaration.start_byte(),
19961        identifier_start: macro_name.start_byte(),
19962        inner_open_start: inner_open.start_byte(),
19963        inner_close_end: inner_close.end_byte(),
19964        outer_close_end: outer_close.end_byte(),
19965        semicolon_end: semicolon.end_byte(),
19966    })
19967}
19968
19969fn cpp_prototype_macro_candidate_from_pointer_expression(
19970    statement: Node<'_>,
19971    source: &str,
19972) -> Option<PrototypeMacroCandidate> {
19973    if statement.kind() != "expression_statement"
19974        || statement.named_child_count() != 1
19975        || !statement.has_error()
19976    {
19977        return None;
19978    }
19979    let expansion = statement
19980        .named_child(0)
19981        .filter(|child| child.kind() == "parameter_pack_expansion")?;
19982    let binary = expansion
19983        .child_by_field_name("pattern")
19984        .filter(|pattern| pattern.kind() == "binary_expression")?;
19985    if binary.child_count() != 3
19986        || binary
19987            .child(1)
19988            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
19989        || expansion
19990            .child(expansion.child_count().checked_sub(1)?)
19991            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
19992    {
19993        return None;
19994    }
19995    let return_type = binary.child_by_field_name("left")?;
19996    let call = binary
19997        .child_by_field_name("right")
19998        .filter(|right| right.kind() == "call_expression")?;
19999    let qualified = call.child_by_field_name("function")?;
20000    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20001    let arguments = call
20002        .child_by_field_name("arguments")
20003        .filter(|arguments| arguments.kind() == "argument_list")?;
20004    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20005    let semicolon = cpp_direct_semicolon(statement)?;
20006    if return_type.end_byte() > declared_name.start_byte()
20007        || macro_name.end_byte() > arguments.start_byte()
20008        || arguments.end_byte() > semicolon.start_byte()
20009    {
20010        return None;
20011    }
20012    Some(cpp_prototype_macro_candidate_from_parts(
20013        statement.start_byte(),
20014        macro_name,
20015        inner,
20016        arguments,
20017        semicolon,
20018    ))
20019}
20020
20021/// The inner parenthesized declarator of the `((parameters))` wrapper the
20022/// macro-declaration grammar makes of a prototype macro's argument list, with
20023/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
20024/// both paren pairs and their ownership come from the CST, and the returned
20025/// node's exact range is the one pair an ordinary function declarator needs.
20026fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20027    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20028        return None;
20029    }
20030    let open = outer.child(0)?;
20031    let close = outer.child(outer.child_count().checked_sub(1)?)?;
20032    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20033        return None;
20034    }
20035    let inner = outer.named_child(0)?;
20036    if inner.kind() != "parenthesized_declarator" {
20037        return None;
20038    }
20039    let inner_open = inner.child(0)?;
20040    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20041    (inner_open.kind() == "("
20042        && !inner_open.is_missing()
20043        && inner_close.kind() == ")"
20044        && !inner_close.is_missing())
20045    .then_some(inner)
20046}
20047
20048/// Whether the macro-declaration grammar admitted a word of this declaration
20049/// as a `macro_modifier`, the structured proof that the declaration did not
20050/// parse without it.
20051fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20052    let mut cursor = declaration.walk();
20053    declaration
20054        .named_children(&mut cursor)
20055        .any(|child| child.kind() == "macro_modifier")
20056}
20057
20058/// `RET name _((args));` as the macro-declaration grammar reads it when the
20059/// argument list stays an expression: the return-type word is admitted as a
20060/// `macro_modifier`, the declared name takes the `type` field, and the macro
20061/// token is the init declarator's callee. Same #2932 contract as the
20062/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
20063fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20064    declaration: Node<'_>,
20065    source: &str,
20066) -> Option<PrototypeMacroCandidate> {
20067    if !cpp_declaration_has_macro_modifier(declaration) {
20068        return None;
20069    }
20070    let declared_name = declaration
20071        .child_by_field_name("type")
20072        .filter(|type_node| type_node.kind() == "type_identifier")?;
20073    let init = declaration
20074        .child_by_field_name("declarator")
20075        .filter(|declarator| declarator.kind() == "init_declarator")?;
20076    let macro_name = init
20077        .child_by_field_name("declarator")
20078        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20079    let arguments = init
20080        .child_by_field_name("value")
20081        .filter(|value| value.kind() == "argument_list")?;
20082    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20083    let semicolon = cpp_direct_semicolon(declaration)?;
20084    if declared_name.end_byte() > macro_name.start_byte()
20085        || macro_name.end_byte() > arguments.start_byte()
20086        || arguments.end_byte() > semicolon.start_byte()
20087    {
20088        return None;
20089    }
20090    Some(cpp_prototype_macro_candidate_from_parts(
20091        declaration.start_byte(),
20092        macro_name,
20093        inner,
20094        arguments,
20095        semicolon,
20096    ))
20097}
20098
20099/// `RET *name _((args));` as the macro-declaration grammar reads it when the
20100/// argument list stays declarator-shaped: the declared name and the macro
20101/// token prefix the nested parenthesized declarator as the modifiers of a
20102/// `macro_modifier_declarator` chain.
20103fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20104    declaration: Node<'_>,
20105    source: &str,
20106) -> Option<PrototypeMacroCandidate> {
20107    let mut declarator = declaration.child_by_field_name("declarator")?;
20108    while declarator.kind() == "pointer_declarator" {
20109        declarator = declarator.child_by_field_name("declarator")?;
20110    }
20111    // The macro's own modifier must be preceded by the declared name's.
20112    let mut saw_name_modifier = false;
20113    let (macro_name, arguments) = loop {
20114        if declarator.kind() != "macro_modifier_declarator" {
20115            return None;
20116        }
20117        let mut cursor = declarator.walk();
20118        let modifier = declarator
20119            .named_children(&mut cursor)
20120            .find(|child| child.kind() == "macro_modifier")
20121            .and_then(macro_modifier_identifier)?;
20122        let inner = declarator.child_by_field_name("declarator")?;
20123        if cpp_is_prototype_macro_identifier(modifier, source) {
20124            break (modifier, inner);
20125        }
20126        saw_name_modifier = true;
20127        declarator = inner;
20128    };
20129    if !saw_name_modifier {
20130        return None;
20131    }
20132    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20133    let semicolon = cpp_direct_semicolon(declaration)?;
20134    Some(cpp_prototype_macro_candidate_from_parts(
20135        declaration.start_byte(),
20136        macro_name,
20137        inner,
20138        arguments,
20139        semicolon,
20140    ))
20141}
20142
20143/// `RET name _((args));` as the macro-declaration grammar reads it when the
20144/// macro token is the one word that could stand as the type: the return-type
20145/// words and the declared name are `macro_modifier`s and the nested argument
20146/// list is the whole declarator.
20147fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20148    declaration: Node<'_>,
20149    source: &str,
20150) -> Option<PrototypeMacroCandidate> {
20151    if !cpp_declaration_has_macro_modifier(declaration) {
20152        return None;
20153    }
20154    let macro_name = declaration
20155        .child_by_field_name("type")
20156        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20157    let arguments = declaration.child_by_field_name("declarator")?;
20158    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20159    let semicolon = cpp_direct_semicolon(declaration)?;
20160    Some(cpp_prototype_macro_candidate_from_parts(
20161        declaration.start_byte(),
20162        macro_name,
20163        inner,
20164        arguments,
20165        semicolon,
20166    ))
20167}
20168
20169/// `RET name _((args));` as the macro-declaration grammar reads it when the
20170/// argument list itself needs recovery: the return-type word is a
20171/// `macro_modifier`, the declared name takes the `type` field, the macro token
20172/// is the first declarator, and the argument list's paren pairs land in the
20173/// bracketing `ERROR`s.
20174fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20175    declaration: Node<'_>,
20176    source: &str,
20177) -> Option<PrototypeMacroCandidate> {
20178    if !cpp_declaration_has_macro_modifier(declaration) {
20179        return None;
20180    }
20181    let declared_name = declaration
20182        .child_by_field_name("type")
20183        .filter(|type_node| type_node.kind() == "type_identifier")?;
20184    let mut declarator_cursor = declaration.walk();
20185    let macro_name = declaration
20186        .children_by_field_name("declarator", &mut declarator_cursor)
20187        .next()
20188        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20189    let open_error = macro_name
20190        .next_named_sibling()
20191        .filter(|next| next.kind() == "ERROR")?;
20192    let close_error = last_named_child(declaration)
20193        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20194    if open_error.child_count() < 3
20195        || open_error
20196            .child(0)
20197            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20198        || open_error
20199            .child(1)
20200            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20201        || close_error.child_count() != 2
20202        || close_error
20203            .child(0)
20204            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20205        || close_error
20206            .child(1)
20207            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20208    {
20209        return None;
20210    }
20211    let inner_open = open_error.child(1)?;
20212    let inner_close = close_error.child(0)?;
20213    let outer_close = close_error.child(1)?;
20214    let semicolon = cpp_direct_semicolon(declaration)?;
20215    if declared_name.end_byte() > macro_name.start_byte()
20216        || macro_name.end_byte() > open_error.start_byte()
20217        || inner_open.start_byte() > inner_close.end_byte()
20218        || inner_close.end_byte() > outer_close.start_byte()
20219        || outer_close.end_byte() > semicolon.start_byte()
20220    {
20221        return None;
20222    }
20223    Some(PrototypeMacroCandidate {
20224        run_start: declaration.start_byte(),
20225        identifier_start: macro_name.start_byte(),
20226        inner_open_start: inner_open.start_byte(),
20227        inner_close_end: inner_close.end_byte(),
20228        outer_close_end: outer_close.end_byte(),
20229        semicolon_end: semicolon.end_byte(),
20230    })
20231}
20232
20233fn cpp_prototype_macro_candidate_from_parts(
20234    run_start: usize,
20235    macro_name: Node<'_>,
20236    inner_arguments: Node<'_>,
20237    outer_arguments: Node<'_>,
20238    semicolon: Node<'_>,
20239) -> PrototypeMacroCandidate {
20240    PrototypeMacroCandidate {
20241        run_start,
20242        identifier_start: macro_name.start_byte(),
20243        inner_open_start: inner_arguments.start_byte(),
20244        inner_close_end: inner_arguments.end_byte(),
20245        outer_close_end: outer_arguments.end_byte(),
20246        semicolon_end: semicolon.end_byte(),
20247    }
20248}
20249
20250/// Recover only CST shapes whose fields preserve the declaration name, macro
20251/// invocation, nested argument list, and terminator. Arbitrary flattened
20252/// `ERROR` wreckage stays unsupported rather than being interpreted through a
20253/// token or delimiter scan (issue #2932).
20254fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20255    let candidate = match node.kind() {
20256        "declaration" | "field_declaration" => {
20257            cpp_prototype_macro_candidate_from_init_declaration(node, source)
20258                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20259                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20260                .or_else(|| {
20261                    cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20262                })
20263                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20264                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20265        }
20266        "expression_statement" => {
20267            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20268        }
20269        _ => None,
20270    };
20271    candidate.into_iter().collect()
20272}
20273
20274fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20275    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20276        return false;
20277    }
20278    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20279        return false;
20280    }
20281    let Some(declarator) = (if node.kind() == "function_definition" {
20282        node.child_by_field_name("declarator")
20283            .and_then(extract_function_declarator)
20284    } else {
20285        node.named_child(0)
20286            .filter(|child| child.kind() == "function_declarator")
20287    }) else {
20288        return false;
20289    };
20290    let Some(name) = cpp_function_declarator_name_node(declarator) else {
20291        return false;
20292    };
20293    declarator.start_byte() == node.start_byte()
20294        && name.kind() == "identifier"
20295        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20296}
20297
20298/// Reparse a fragmented class-body interior while preserving its original byte
20299/// and line offsets, confined to the region by tree-sitter included ranges.
20300///
20301/// This used to materialize the region's whole file prefix as whitespace and
20302/// make the lexer walk it, the technique #1309 replaced on the other reparse
20303/// path: O(file) per fragmented-class recovery, on files that are already
20304/// error-recovered and already slow (#2788). Included ranges give the parser
20305/// the same view -- the region's bytes, at their original offsets and
20306/// line/column positions -- without materializing or lexing anything before it.
20307///
20308/// The equality of the two views is the claim, so a debug build parses both and
20309/// asserts the trees agree node for node.
20310fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20311    let region = cpp_reparse_region_items(source, start, end);
20312
20313    #[cfg(debug_assertions)]
20314    assert_eq!(
20315        region.as_ref().map(cpp_tree_shape),
20316        cpp_reparse_padded_class_body(source, start, end)
20317            .as_ref()
20318            .map(cpp_tree_shape),
20319        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20320         prefix produces"
20321    );
20322
20323    region
20324}
20325
20326/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
20327/// replaces, kept as the oracle a debug build asserts every region reparse
20328/// against and as the release-mode parity tests' reference (#2788).
20329#[cfg(any(debug_assertions, test))]
20330fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20331    if start >= end {
20332        // The included-range parser refuses an empty region; the padded one
20333        // returned a tree holding nothing, which every caller read as "no
20334        // members here".
20335        return None;
20336    }
20337    let bytes = source.as_bytes();
20338    let prefix = bytes.get(..start)?;
20339    let interior = bytes.get(start..end)?;
20340    let mut padded = Vec::with_capacity(end);
20341    padded.extend(
20342        prefix
20343            .iter()
20344            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20345    );
20346    padded.extend_from_slice(interior);
20347    let padded = String::from_utf8(padded).ok()?;
20348    let mut parser = tree_sitter::Parser::new();
20349    parser
20350        .set_language(&tree_sitter_cpp::LANGUAGE.into())
20351        .ok()?;
20352    parser.parse(&padded, None)
20353}
20354
20355/// Every node of `tree` in preorder, by kind, span and position, which is what
20356/// two reparses of one region have to agree on for their callers to read the
20357/// same declarations out of either (#2788).
20358#[cfg(any(debug_assertions, test))]
20359fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20360    let mut shape = Vec::new();
20361    let mut cursor = tree.root_node().walk();
20362    let mut stack = vec![tree.root_node()];
20363    while let Some(node) = stack.pop() {
20364        shape.push((
20365            node.kind(),
20366            node.start_byte(),
20367            node.end_byte(),
20368            node.start_position().row,
20369            node.start_position().column,
20370            node.is_named(),
20371            node.is_missing(),
20372        ));
20373        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20374        stack.extend(children.into_iter().rev());
20375    }
20376    shape
20377}
20378
20379/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
20380/// reparsed interior is indexed only when every top-level named node is a
20381/// well-formed C++ item (or a comment) and at least one real item is present.
20382/// Expression/statement soup surfaces as a top-level `ERROR` or
20383/// `expression_statement`, neither of which is an item kind, so it is rejected.
20384///
20385/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
20386/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
20387/// swallowed by a preceding dangling sentinel) reparses to a real
20388/// `namespace_definition` whose body still holds a bogus `function_definition`,
20389/// so the subtree legitimately carries an error. Container items are admitted
20390/// even with an internal error; the inner bogus function is recovered recursively
20391/// when `visit_function_definition` walks it. Each recursion strips at least one
20392/// leading sentinel, so the region strictly shrinks and recovery terminates.
20393///
20394/// A top-level `function_definition` is the one place we stay strict: it is
20395/// admitted only when it is clean or is itself a sentinel candidate. A function
20396/// that has an error and is not a sentinel is a real callable with a broken body,
20397/// so we refuse the whole reparse and let the ordinary path handle it (preserving
20398/// its real return type rather than re-deriving an implicit one).
20399fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20400    let mut cursor = root.walk();
20401    let mut saw_item = false;
20402    for child in root.named_children(&mut cursor) {
20403        match child.kind() {
20404            "comment" => {}
20405            "function_definition" => {
20406                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20407                    return false;
20408                }
20409                saw_item = true;
20410            }
20411            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20412            _ => return false,
20413        }
20414    }
20415    saw_item
20416}
20417
20418/// Robustness gate for a reparsed fragmented multiple-base export class body
20419/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
20420/// kinds a class body produces when reparsed at translation-unit scope: the
20421/// access-specifier label preceding the first member surfaces as a
20422/// `labeled_statement` wrapping that member, and members surface as
20423/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
20424/// Statement or expression soup surfaces as other top-level kinds and is rejected,
20425/// so only a genuinely member-shaped body is ever re-owned as members; anything
20426/// ambiguous falls back to indexing the class alone.
20427fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20428    if node.kind() != "ERROR" {
20429        return false;
20430    }
20431    let mut stack = Vec::new();
20432    let mut saw_function_declarator = false;
20433    let mut cursor = node.walk();
20434    for child in node.named_children(&mut cursor) {
20435        stack.push(child);
20436    }
20437    while let Some(current) = stack.pop() {
20438        match current.kind() {
20439            // Tree-sitter may wrap adjacent copy-control declarations in a
20440            // nested ERROR. Keep descending only through ERROR wrappers; the
20441            // actual declaration payload must be a function_declarator.
20442            "ERROR" => {
20443                let mut cursor = current.walk();
20444                stack.extend(current.named_children(&mut cursor));
20445            }
20446            "function_declarator" => saw_function_declarator = true,
20447            _ => return false,
20448        }
20449    }
20450    saw_function_declarator
20451}
20452
20453fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20454    if node.kind() != "ERROR" {
20455        return false;
20456    }
20457    let mut cursor = node.walk();
20458    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20459    let [explicit, constructor_error, tail] = named.as_slice() else {
20460        return false;
20461    };
20462    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20463        return false;
20464    }
20465    // The copy-control declarators the inner ERROR holds, with only their `;`
20466    // tokens beside them. The older grammar kept the constructor alone there
20467    // and gave the destructor the slot after it; the macro-declaration grammar
20468    // absorbs the destructor beside the constructor and folds the member
20469    // written after them into the tail slot as a `macro_modifier_declarator`.
20470    let mut declarators = Vec::new();
20471    let mut stack = vec![*constructor_error];
20472    while let Some(current) = stack.pop() {
20473        match current.kind() {
20474            "ERROR" => {
20475                let mut cursor = current.walk();
20476                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20477                stack.extend(children.into_iter().rev());
20478            }
20479            "function_declarator" => declarators.push(current),
20480            _ => return false,
20481        }
20482    }
20483    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20484        ([constructor], "function_declarator") => (*constructor, *tail),
20485        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20486        _ => return false,
20487    };
20488    let Some(constructor_name) =
20489        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20490    else {
20491        return false;
20492    };
20493    let Some(destructor_name) =
20494        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20495    else {
20496        return false;
20497    };
20498    let Some(destroyed_type) = destructor_name.named_child(0) else {
20499        return false;
20500    };
20501    constructor_name.kind() == "identifier"
20502        && destructor_name.kind() == "destructor_name"
20503        && destroyed_type.kind() == "identifier"
20504        && node_text(constructor_name, source) == node_text(destroyed_type, source)
20505}
20506
20507fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20508    if node.kind() != "compound_statement" {
20509        return false;
20510    }
20511    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20512        return false;
20513    };
20514    if prefix.kind() == "labeled_statement"
20515        && prefix.named_child(0).is_some_and(|label| {
20516            matches!(
20517                node_text(label, source).trim(),
20518                "public" | "private" | "protected"
20519            )
20520        })
20521    {
20522        return prefix.named_children(&mut prefix.walk()).any(|child| {
20523            child.kind() == "declaration"
20524                && child.has_error()
20525                && child
20526                    .named_children(&mut child.walk())
20527                    .any(cpp_reparsed_member_error_is_indexable)
20528        });
20529    }
20530    // A malformed constructor initializer can be split into a declaration
20531    // followed by its compound body when the class prefix already contains
20532    // realistic members. Keep this admission tied to that exact structured
20533    // declaration/error/body chain rather than accepting arbitrary blocks.
20534    prefix.kind() == "declaration"
20535        && prefix.has_error()
20536        && prefix
20537            .named_children(&mut prefix.walk())
20538            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20539}
20540
20541fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20542    if !cpp_reparsed_member_error_is_indexable(node) {
20543        return false;
20544    }
20545    let Some(preproc) = node.next_named_sibling() else {
20546        return false;
20547    };
20548    preproc.kind() == "preproc_if"
20549        && preproc.has_error()
20550        && preproc
20551            .named_children(&mut preproc.walk())
20552            .any(|child| child.kind() == "expression_statement" && child.has_error())
20553        && preproc
20554            .next_named_sibling()
20555            .is_some_and(|body| body.kind() == "compound_statement")
20556}
20557
20558/// Return a function body whose braces and ownership are explicit in the
20559/// reparsed class-member tree. An error below a real function envelope is
20560/// recoverable by the ordinary function visitor; a missing/deferred body is
20561/// not, because accepting it would let statement soup masquerade as a member.
20562fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20563    if node.kind() != "function_definition" {
20564        return None;
20565    }
20566    let body = node.child_by_field_name("body")?;
20567    if body.kind() != "compound_statement" {
20568        return None;
20569    }
20570    let open = body.child(0)?;
20571    let close = body.child(body.child_count().checked_sub(1)?)?;
20572    if open.kind() != "{"
20573        || open.is_missing()
20574        || close.kind() != "}"
20575        || close.is_missing()
20576        || close.end_byte() != body.end_byte()
20577        || body.end_byte() != node.end_byte()
20578    {
20579        return None;
20580    }
20581    Some(body)
20582}
20583
20584fn cpp_reparsed_member_function_errors_are_in_body(
20585    node: Node<'_>,
20586    body: Node<'_>,
20587    source: &str,
20588) -> bool {
20589    let mut cursor = node.walk();
20590    node.children(&mut cursor).all(|child| {
20591        same_node(child, body)
20592            || cpp_reparsed_member_attribute_error(child, source)
20593            || cpp_reparsed_member_signature_identifier_errors(child)
20594            || (!child.has_error() && !child.is_error() && !child.is_missing())
20595    })
20596}
20597
20598/// A complete callable can still carry parser errors in its signature when a
20599/// project annotation is not part of the C++ grammar (`nonneg int`,
20600/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
20601/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
20602/// leaves inside the already-proven callable envelope; structured statements,
20603/// literals, missing tokens, and other malformed signature payload remain
20604/// rejected.
20605fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
20606    if !node.has_error() && !node.is_error() && !node.is_missing() {
20607        return false;
20608    }
20609    let mut stack = vec![node];
20610    let mut saw_error = false;
20611    while let Some(current) = stack.pop() {
20612        if current.is_missing() {
20613            return false;
20614        }
20615        if current.kind() == "ERROR" {
20616            saw_error = true;
20617            let mut cursor = current.walk();
20618            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20619            if children
20620                .iter()
20621                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
20622            {
20623                return false;
20624            }
20625            stack.extend(children);
20626            continue;
20627        }
20628        let mut cursor = current.walk();
20629        stack.extend(current.children(&mut cursor));
20630    }
20631    saw_error
20632}
20633
20634fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
20635    node.kind() == "ERROR"
20636        && node.named_child_count() == 1
20637        && node
20638            .named_child(0)
20639            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
20640}
20641
20642fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
20643    node.kind() == "identifier"
20644        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20645}
20646
20647/// A C++ attribute placed between a member's declarator and body can make
20648/// tree-sitter expose the callable as
20649/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
20650/// Keep this admission tied to that exact node geometry. In particular, an
20651/// arbitrary ERROR or identifier before a compound statement is not enough.
20652fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
20653    let Some(body) = cpp_reparsed_member_function_body(node) else {
20654        return false;
20655    };
20656    let mut cursor = node.walk();
20657    let named = node
20658        .named_children(&mut cursor)
20659        .filter(|child| child.kind() != "comment")
20660        .collect::<Vec<_>>();
20661    let [type_node, error, attribute, body_node] = named.as_slice() else {
20662        return false;
20663    };
20664    if !same_node(*body_node, body)
20665        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20666        || !cpp_reparsed_export_macro_identifier(*attribute, source)
20667        || error.kind() != "ERROR"
20668        || error.named_child_count() != 1
20669    {
20670        return false;
20671    }
20672    error
20673        .named_child(0)
20674        .is_some_and(cpp_reparsed_attribute_callable_declarator)
20675}
20676
20677fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
20678    cpp_structured_type_path(node, source).is_some()
20679        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
20680}
20681
20682fn cpp_reparsed_member_return_and_declarator_are_indexable(
20683    return_error: Node<'_>,
20684    declarator: Node<'_>,
20685    body_node: Node<'_>,
20686    body: Node<'_>,
20687    source: &str,
20688) -> bool {
20689    return_error.kind() == "ERROR"
20690        && return_error.named_child_count() == 1
20691        && same_node(body_node, body)
20692        && return_error.named_child(0).is_some_and(|return_type| {
20693            cpp_reparsed_member_return_type_is_indexable(return_type, source)
20694        })
20695        && extract_function_declarator(declarator)
20696            .and_then(cpp_function_declarator_name_node)
20697            .is_some()
20698}
20699
20700fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20701    let Some(body) = cpp_reparsed_member_function_body(node) else {
20702        return false;
20703    };
20704    let mut cursor = node.walk();
20705    let named = node
20706        .named_children(&mut cursor)
20707        .filter(|child| child.kind() != "comment")
20708        .collect::<Vec<_>>();
20709    let [friend, return_error, declarator, body_node] = named.as_slice() else {
20710        return false;
20711    };
20712    friend.kind() == "type_identifier"
20713        && node_text(*friend, source) == "friend"
20714        && cpp_reparsed_member_return_and_declarator_are_indexable(
20715            *return_error,
20716            *declarator,
20717            *body_node,
20718            body,
20719            source,
20720        )
20721}
20722
20723fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20724    let Some(body) = cpp_reparsed_member_function_body(node) else {
20725        return false;
20726    };
20727    let mut cursor = node.walk();
20728    let named = node
20729        .named_children(&mut cursor)
20730        .filter(|child| child.kind() != "comment")
20731        .collect::<Vec<_>>();
20732    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
20733        return false;
20734    };
20735    prefix
20736        .iter()
20737        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
20738        && attribute.kind() == "type_identifier"
20739        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20740        && cpp_reparsed_member_return_and_declarator_are_indexable(
20741            *return_error,
20742            *declarator,
20743            *body_node,
20744            body,
20745            source,
20746        )
20747}
20748
20749/// An included-range reparse that begins inside a malformed class can merge an
20750/// access label and following template member. Tree-sitter then emits the label
20751/// as the `template_type` name, the template parameter list as its arguments,
20752/// an ERROR-wrapped return type, the callable declarator, and its complete
20753/// body. Admit only that exact structured displacement.
20754fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
20755    let Some(body) = cpp_reparsed_member_function_body(node) else {
20756        return false;
20757    };
20758    let mut cursor = node.walk();
20759    let named = node
20760        .named_children(&mut cursor)
20761        .filter(|child| child.kind() != "comment")
20762        .collect::<Vec<_>>();
20763    // The member's return type is an `ERROR` of its own, or -- under the
20764    // macro-declaration grammar, which admits it as a modifier of the
20765    // declarator it could not separate -- folded into the declarator's
20766    // `macro_modifier_declarator` wrapper.
20767    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
20768        [template_type, return_error, declarator, body_node] => {
20769            let Some(return_type) = return_error.named_child(0) else {
20770                return false;
20771            };
20772            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
20773                return false;
20774            }
20775            (*template_type, return_type, *declarator, *body_node)
20776        }
20777        [template_type, declarator, body_node] => {
20778            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
20779                .then(|| {
20780                    let mut cursor = declarator.walk();
20781                    declarator
20782                        .named_children(&mut cursor)
20783                        .find(|child| child.kind() == "macro_modifier")
20784                })
20785                .flatten()
20786                .and_then(macro_modifier_identifier)
20787            else {
20788                return false;
20789            };
20790            let Some(function) = declarator.child_by_field_name("declarator") else {
20791                return false;
20792            };
20793            (*template_type, return_type, function, *body_node)
20794        }
20795        _ => return false,
20796    };
20797    let Some(template_name) = template_type.child_by_field_name("name") else {
20798        return false;
20799    };
20800    let Some(arguments) = template_type.child_by_field_name("arguments") else {
20801        return false;
20802    };
20803    let mut cursor = template_type.walk();
20804    let template_errors = template_type
20805        .named_children(&mut cursor)
20806        .filter(|child| child.kind() == "ERROR")
20807        .collect::<Vec<_>>();
20808    let [comment_error] = template_errors.as_slice() else {
20809        return false;
20810    };
20811    let mut cursor = comment_error.walk();
20812    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
20813    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
20814        return false;
20815    };
20816    same_node(body_node, body)
20817        && template_type.kind() == "template_type"
20818        && template_name.kind() == "type_identifier"
20819        && matches!(
20820            node_text(template_name, source).trim(),
20821            "public" | "private" | "protected"
20822        )
20823        && arguments.kind() == "template_argument_list"
20824        && arguments.named_child_count() > 0
20825        && !arguments.has_error()
20826        && !colon.is_named()
20827        && colon.kind() == ":"
20828        && comments.iter().all(|child| child.kind() == "comment")
20829        && !template_keyword.is_named()
20830        && template_keyword.kind() == "template"
20831        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
20832        && extract_function_declarator(declarator)
20833            .and_then(cpp_function_declarator_name_node)
20834            .is_some()
20835}
20836
20837/// Return the constructor declaration tree-sitter can merge into an access
20838/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
20839/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
20840/// declaration's apparent type; the callable name must still exactly match the
20841/// recovered class, so unrelated labeled statements are never re-owned.
20842fn cpp_reparsed_preprocessor_constructor<'tree>(
20843    node: Node<'tree>,
20844    class_name: &str,
20845    source: &str,
20846) -> Option<Node<'tree>> {
20847    if node.kind() != "labeled_statement" {
20848        return None;
20849    }
20850    let mut cursor = node.walk();
20851    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20852    let [label, directive_error, declaration] = named.as_slice() else {
20853        return None;
20854    };
20855    if label.kind() != "statement_identifier"
20856        || !matches!(
20857            node_text(*label, source),
20858            "public" | "private" | "protected"
20859        )
20860        || directive_error.kind() != "ERROR"
20861        || directive_error.child_count() != 1
20862        || directive_error
20863            .child(0)
20864            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
20865        || declaration.kind() != "declaration"
20866        || declaration.named_child_count() != 2
20867    {
20868        return None;
20869    }
20870    let apparent_type = declaration.child_by_field_name("type")?;
20871    if apparent_type.kind() != "type_identifier"
20872        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
20873    {
20874        return None;
20875    }
20876    let declarator = declaration.child_by_field_name("declarator")?;
20877    let function = extract_function_declarator(declarator)?;
20878    let name = cpp_function_declarator_name_node(function)?;
20879    (node_text(name, source) == class_name).then_some(*declaration)
20880}
20881
20882fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
20883    if extract_function_declarator(node)
20884        .and_then(cpp_function_declarator_name_node)
20885        .is_some()
20886    {
20887        return true;
20888    }
20889    node.kind() == "init_declarator"
20890        && node
20891            .child_by_field_name("declarator")
20892            .is_some_and(|declarator| declarator.kind() == "identifier")
20893        && node
20894            .child_by_field_name("value")
20895            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
20896}
20897
20898/// Return true for the constrained/attribute form that tree-sitter splits into
20899/// an ERROR declaration, a preprocessor `requires` clause, and a following
20900/// compound statement. The three nodes must remain immediate named siblings;
20901/// this deliberately does not search source text or skip unrelated statements.
20902fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
20903    if node.kind() != "ERROR" || node.named_child_count() != 3 {
20904        return false;
20905    }
20906    let mut cursor = node.walk();
20907    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20908    let [type_node, function_declarator, attribute] = named.as_slice() else {
20909        return false;
20910    };
20911    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
20912        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
20913        || attribute.kind() != "identifier"
20914        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
20915    {
20916        return false;
20917    }
20918    let Some(preproc) =
20919        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20920    else {
20921        return false;
20922    };
20923    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
20924        .filter(|sibling| sibling.kind() == "compound_statement")
20925    else {
20926        return false;
20927    };
20928    let Some(open) = body.child(0) else {
20929        return false;
20930    };
20931    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
20932        return false;
20933    };
20934    let Some(condition) = preproc.child_by_field_name("condition") else {
20935        return false;
20936    };
20937    let mut cursor = preproc.walk();
20938    let payload = preproc
20939        .named_children(&mut cursor)
20940        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
20941        .collect::<Vec<_>>();
20942    let [requires_statement] = payload.as_slice() else {
20943        return false;
20944    };
20945    let requires_clause = requires_statement.named_child(0);
20946
20947    open.kind() == "{"
20948        && !open.is_missing()
20949        && close.kind() == "}"
20950        && !close.is_missing()
20951        && close.end_byte() == body.end_byte()
20952        && requires_statement.kind() == "expression_statement"
20953        && requires_statement.named_child_count() == 1
20954        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
20955}
20956
20957fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20958    let mut sibling = node.next_named_sibling();
20959    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20960        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
20961    }
20962    sibling
20963}
20964
20965fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
20966    let mut sibling = node.prev_named_sibling();
20967    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
20968        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
20969    }
20970    sibling
20971}
20972
20973fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
20974    let Some(preproc) =
20975        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
20976    else {
20977        return false;
20978    };
20979    let Some(error) =
20980        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
20981    else {
20982        return false;
20983    };
20984    cpp_reparsed_attribute_requires_error(error, source)
20985}
20986
20987fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
20988    parameter: Node<'tree>,
20989    macro_name: Node<'tree>,
20990    message: Node<'tree>,
20991    source: &str,
20992) -> Option<Node<'tree>> {
20993    let parameter_name = parameter.named_child(0)?;
20994    (parameter.kind() == "type_parameter_declaration"
20995        && parameter_name.kind() == "type_identifier"
20996        && matches!(macro_name.kind(), "identifier" | "type_identifier")
20997        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
20998        && message.kind() == "string_literal")
20999        .then_some(parameter_name)
21000}
21001
21002fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21003    node: Node<'tree>,
21004    source: &str,
21005) -> Option<Node<'tree>> {
21006    if node.kind() != "ERROR" {
21007        return None;
21008    }
21009    let mut cursor = node.walk();
21010    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21011    let [parameter, macro_name, message] = named.as_slice() else {
21012        return None;
21013    };
21014    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21015}
21016
21017/// Recognize the alternate constraint-macro prefix where tree-sitter retains
21018/// the complete qualified constraint as a fourth child instead of moving it
21019/// into the following function. Keep the gate tied to a two-type template
21020/// constraint that names the declared type parameter.
21021fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21022    node: Node<'tree>,
21023    source: &str,
21024) -> Option<Node<'tree>> {
21025    if node.kind() != "ERROR" {
21026        return None;
21027    }
21028    let mut cursor = node.walk();
21029    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21030    let [parameter, macro_name, message, constraint] = named.as_slice() else {
21031        return None;
21032    };
21033    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21034        *parameter,
21035        *macro_name,
21036        *message,
21037        source,
21038    )?;
21039    let constraint_scope = constraint.child_by_field_name("scope")?;
21040    let constraint_template = constraint.child_by_field_name("name")?;
21041    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21042    let mut argument_cursor = constraint_arguments.walk();
21043    let constraint_types = constraint_arguments
21044        .named_children(&mut argument_cursor)
21045        .collect::<Vec<_>>();
21046    if constraint.kind() != "qualified_identifier"
21047        || constraint_scope.kind() != "namespace_identifier"
21048        || !matches!(
21049            constraint_template.kind(),
21050            "template_function" | "template_type"
21051        )
21052        || !matches!(constraint_types.as_slice(), [left, right]
21053            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21054        || constraint_arguments.has_error()
21055    {
21056        return None;
21057    }
21058    let parameter_text = node_text(parameter_name, source);
21059    let mut stack = constraint_types;
21060    while let Some(current) = stack.pop() {
21061        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21062            return Some(parameter_name);
21063        }
21064        let mut cursor = current.walk();
21065        stack.extend(current.named_children(&mut cursor));
21066    }
21067    None
21068}
21069
21070fn cpp_reparsed_template_macro_companion_is_indexable(
21071    node: Node<'_>,
21072    parameter_name: Node<'_>,
21073    source: &str,
21074) -> bool {
21075    let Some(body) = cpp_reparsed_member_function_body(node) else {
21076        return false;
21077    };
21078    let mut cursor = node.walk();
21079    let named = node
21080        .named_children(&mut cursor)
21081        .filter(|child| child.kind() != "comment")
21082        .collect::<Vec<_>>();
21083    // The member's return type is an `ERROR` of its own, or -- under the
21084    // macro-declaration grammar, which admits it as a modifier of the
21085    // declarator it could not separate -- folded into the declarator's
21086    // `macro_modifier_declarator` wrapper.
21087    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21088        .as_slice()
21089    {
21090        [
21091            constraint,
21092            close_error,
21093            storage,
21094            return_error,
21095            declarator,
21096            body_node,
21097        ] => {
21098            let Some(return_type) = return_error.named_child(0) else {
21099                return false;
21100            };
21101            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21102                return false;
21103            }
21104            (
21105                *constraint,
21106                *close_error,
21107                *storage,
21108                return_type,
21109                *declarator,
21110                *body_node,
21111            )
21112        }
21113        [constraint, close_error, storage, declarator, body_node] => {
21114            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21115                return false;
21116            };
21117            (
21118                *constraint,
21119                *close_error,
21120                *storage,
21121                return_type,
21122                function,
21123                *body_node,
21124            )
21125        }
21126        _ => return false,
21127    };
21128    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21129        return false;
21130    };
21131    let Some(constraint_template) = constraint.child_by_field_name("name") else {
21132        return false;
21133    };
21134    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21135        return false;
21136    };
21137    let mut cursor = constraint_arguments.walk();
21138    let constraint_types = constraint_arguments
21139        .named_children(&mut cursor)
21140        .collect::<Vec<_>>();
21141    same_node(body_node, body)
21142        && constraint.kind() == "qualified_identifier"
21143        && constraint_scope.kind() == "namespace_identifier"
21144        && constraint_template.kind() == "template_type"
21145        && matches!(constraint_types.as_slice(), [left, right]
21146            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21147        && !constraint_arguments.has_error()
21148        && close_error.kind() == "ERROR"
21149        && close_error.named_child_count() == 0
21150        && storage.kind() == "storage_class_specifier"
21151        && return_type.kind() == "identifier"
21152        && node_text(return_type, source) == node_text(parameter_name, source)
21153        && extract_function_declarator(declarator)
21154            .and_then(cpp_function_declarator_name_node)
21155            .is_some()
21156}
21157
21158fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21159    node: Node<'tree>,
21160    parameter_name: Node<'_>,
21161    source: &str,
21162) -> Option<Node<'tree>> {
21163    let body = cpp_reparsed_member_function_body(node)?;
21164    let constraint = node.child_by_field_name("type")?;
21165    let constraint_template = constraint.child_by_field_name("name")?;
21166    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21167    let mut argument_cursor = constraint_arguments.walk();
21168    let constraint_types = constraint_arguments
21169        .named_children(&mut argument_cursor)
21170        .collect::<Vec<_>>();
21171    if constraint.kind() != "qualified_identifier"
21172        || constraint_template.kind() != "template_type"
21173        || !matches!(constraint_types.as_slice(), [left, right]
21174            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21175        || constraint_arguments.has_error()
21176        || node
21177            .child_by_field_name("body")
21178            .is_none_or(|candidate| !same_node(candidate, body))
21179    {
21180        return None;
21181    }
21182
21183    let mut cursor = node.walk();
21184    let recovery_errors = node
21185        .named_children(&mut cursor)
21186        .filter(|child| child.kind() == "ERROR")
21187        .collect::<Vec<_>>();
21188    if !recovery_errors
21189        .iter()
21190        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21191        || !recovery_errors.iter().all(|error| {
21192            error.named_child_count() == 0
21193                || cpp_reparsed_constraint_macro_error(*error, source)
21194                || (error.named_child_count() == 1
21195                    && error
21196                        .named_child(0)
21197                        .is_some_and(|child| child.kind() == "function_declarator"))
21198        })
21199    {
21200        return None;
21201    }
21202
21203    let parameter_text = node_text(parameter_name, source);
21204    let mut declarators = node
21205        .child_by_field_name("declarator")
21206        .and_then(extract_function_declarator)
21207        .into_iter()
21208        .collect::<Vec<_>>();
21209    for error in recovery_errors {
21210        let mut stack = vec![error];
21211        while let Some(current) = stack.pop() {
21212            if current.kind() == "function_declarator" {
21213                declarators.push(current);
21214            }
21215            let mut cursor = current.walk();
21216            stack.extend(current.named_children(&mut cursor));
21217        }
21218    }
21219    declarators.into_iter().find(|declarator| {
21220        cpp_function_declarator_name_node(*declarator)
21221            .is_some_and(|name| name.kind() == "identifier")
21222            && declarator
21223                .child_by_field_name("parameters")
21224                .is_some_and(|parameters| {
21225                    parameters
21226                        .named_children(&mut parameters.walk())
21227                        .filter_map(|parameter| parameter.child_by_field_name("type"))
21228                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21229                })
21230    })
21231}
21232
21233fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21234    node: Node<'_>,
21235    parameter_name: Node<'_>,
21236    source: &str,
21237) -> bool {
21238    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21239}
21240
21241fn cpp_reparsed_template_macro_function_companion_is_indexable(
21242    node: Node<'_>,
21243    parameter_name: Node<'_>,
21244    source: &str,
21245) -> bool {
21246    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21247        return false;
21248    }
21249    let Some(return_type) = node.child_by_field_name("type") else {
21250        return false;
21251    };
21252    let Some(function_declarator) = node
21253        .child_by_field_name("declarator")
21254        .and_then(extract_function_declarator)
21255    else {
21256        return false;
21257    };
21258    if cpp_function_declarator_name_node(function_declarator).is_none()
21259        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21260    {
21261        return false;
21262    }
21263    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21264        return false;
21265    };
21266    let parameter_text = node_text(parameter_name, source);
21267    parameters
21268        .named_children(&mut parameters.walk())
21269        .any(|parameter| {
21270            parameter
21271                .child_by_field_name("type")
21272                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21273        })
21274}
21275
21276fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21277    if node.kind() != "ERROR" {
21278        return false;
21279    }
21280    let mut stack = vec![node];
21281    while let Some(current) = stack.pop() {
21282        let macro_shape = match current.kind() {
21283            "call_expression" => current
21284                .child_by_field_name("function")
21285                .zip(current.child_by_field_name("arguments")),
21286            "init_declarator" => current
21287                .child_by_field_name("declarator")
21288                .zip(current.child_by_field_name("value")),
21289            _ => None,
21290        };
21291        if let Some((name, arguments)) = macro_shape
21292            && name.kind() == "identifier"
21293            && arguments.kind() == "argument_list"
21294            && arguments.named_child_count() >= 2
21295            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21296        {
21297            return true;
21298        }
21299        let mut cursor = current.walk();
21300        stack.extend(current.named_children(&mut cursor));
21301    }
21302    false
21303}
21304
21305fn cpp_recovered_template_macro_constructor<'tree>(
21306    node: Node<'tree>,
21307    source: &str,
21308) -> Option<(Node<'tree>, Node<'tree>)> {
21309    let mut prefix = node.prev_named_sibling()?;
21310    while prefix.kind() == "comment" {
21311        prefix = prefix.prev_named_sibling()?;
21312    }
21313    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21314    let parameter = parameter_name
21315        .parent()
21316        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21317    let declarator =
21318        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21319    Some((declarator, parameter))
21320}
21321
21322/// The template parameter a split constraint-macro head declares for the
21323/// member function after it, when the recovery gate admits the pair.
21324/// `template <class U, REQUIRES("...", std::is_convertible<U, int>)>` leaves
21325/// the head as an `ERROR` beside a clean function definition; the parameter is
21326/// the head's structured `type_parameter_declaration`.
21327fn cpp_recovered_template_macro_function_parameter<'tree>(
21328    node: Node<'tree>,
21329    source: &str,
21330) -> Option<Node<'tree>> {
21331    let prefix = cpp_prev_non_comment_named_sibling(node)?;
21332    let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21333    if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21334        return None;
21335    }
21336    parameter_name
21337        .parent()
21338        .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21339}
21340
21341fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21342    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21343        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21344            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21345                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21346                    function,
21347                    parameter_name,
21348                    source,
21349                )
21350        });
21351    }
21352    let Some(parameter_name) =
21353        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21354    else {
21355        return false;
21356    };
21357    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21358        cpp_reparsed_template_macro_function_companion_is_indexable(
21359            function,
21360            parameter_name,
21361            source,
21362        )
21363    })
21364}
21365
21366fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21367    let function_name = node
21368        .child_by_field_name("declarator")
21369        .and_then(extract_function_declarator)
21370        .and_then(cpp_function_declarator_name_node);
21371    if let Some(body) = cpp_reparsed_member_function_body(node)
21372        && function_name.is_some()
21373        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21374    {
21375        return true;
21376    }
21377    cpp_reparsed_attribute_member_function(node, source)
21378        || cpp_reparsed_friend_function_is_indexable(node, source)
21379        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21380        || cpp_reparsed_access_template_function_is_indexable(node, source)
21381        || cpp_recovered_template_macro_constructor(node, source).is_some()
21382}
21383
21384/// Recognize the three top-level nodes produced when an unknown attribute
21385/// macro separates an inline member's declarator from its body in a reparsed
21386/// class interior: an errorful declaration with a missing semicolon, the macro
21387/// call expression, and the complete compound body. Their adjacency and exact
21388/// structured shapes prove one recoverable member envelope; arbitrary calls or
21389/// blocks do not pass this gate.
21390fn cpp_reparsed_macro_attribute_member_sequence(
21391    children: &[Node<'_>],
21392    index: usize,
21393    source: &str,
21394) -> bool {
21395    let Some(prefix) = children.get(index).copied() else {
21396        return false;
21397    };
21398    let declaration = if prefix.kind() == "labeled_statement" {
21399        prefix
21400            .named_child(prefix.named_child_count().saturating_sub(1))
21401            .filter(|child| child.kind() == "declaration")
21402    } else {
21403        (prefix.kind() == "declaration").then_some(prefix)
21404    };
21405    let Some(declaration) = declaration else {
21406        return false;
21407    };
21408    if !declaration.has_error()
21409        || declaration
21410            .child_by_field_name("declarator")
21411            .and_then(extract_function_declarator)
21412            .and_then(cpp_function_declarator_name_node)
21413            .is_none()
21414    {
21415        return false;
21416    }
21417    let Some(attribute_statement) = children.get(index + 1).copied() else {
21418        return false;
21419    };
21420    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21421        .then(|| attribute_statement.named_child(0))
21422        .flatten()
21423        .filter(|child| child.kind() == "call_expression")
21424    else {
21425        return false;
21426    };
21427    let Some(attribute_name) = attribute_call
21428        .child_by_field_name("function")
21429        .filter(|function| function.kind() == "identifier")
21430        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21431    else {
21432        return false;
21433    };
21434    if !cpp_export_macro_token(&attribute_name) {
21435        return false;
21436    }
21437    let Some(body) = children.get(index + 2).copied() else {
21438        return false;
21439    };
21440    body.kind() == "compound_statement"
21441        && body.child(0).is_some_and(|open| open.kind() == "{")
21442        && body
21443            .child(body.child_count().saturating_sub(1))
21444            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21445        && declaration.end_byte() <= attribute_statement.start_byte()
21446        && attribute_statement.end_byte() <= body.start_byte()
21447}
21448
21449/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
21450/// types and specifiers followed by a declarator, over and over, with nothing
21451/// left over. A string-argument attribute macro is what strands them there, and
21452/// the walk indexes exactly what this reads, so admitting the region is safe
21453/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
21454/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
21455/// the whole class body.
21456fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21457    if node.kind() != "ERROR" {
21458        return false;
21459    }
21460    let run = stranded_declaration_run(node, source);
21461    run.complete && !run.declarations.is_empty()
21462}
21463
21464/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
21465/// translation-unit-shaped class-body reparse as an assignment expression:
21466/// `Ctor() = default`. Admit only the exact assignment shape, and only when
21467/// the same reparse retains a separate constructor declarator for that name.
21468/// The second condition keeps an arbitrary expression statement from becoming
21469/// a member merely because it contains a callee followed by `default`.
21470/// A constructor declaration a statement-context class-body reparse reads as
21471/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
21472/// constructor declaration is indistinguishable from a call, but a direct
21473/// child of the reparse root whose callee names the class can only be the
21474/// constructor: a member with no return type is nothing else.
21475fn cpp_reparsed_bare_constructor_call<'tree>(
21476    node: Node<'tree>,
21477    class_name: &str,
21478    source: &str,
21479) -> Option<Node<'tree>> {
21480    if node.kind() != "expression_statement" {
21481        return None;
21482    }
21483    let call = node
21484        .named_child(0)
21485        .filter(|child| child.kind() == "call_expression")?;
21486    let function = call.child_by_field_name("function")?;
21487    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21488        .then_some(call)
21489}
21490
21491fn cpp_reparsed_defaulted_constructor_is_indexable(
21492    node: Node<'_>,
21493    members: &[Node<'_>],
21494    class_name: Option<&str>,
21495    source: &str,
21496) -> bool {
21497    let Some(class_name) = class_name else {
21498        return false;
21499    };
21500    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21501        return false;
21502    }
21503    let Some(assignment) = node.named_child(0) else {
21504        return false;
21505    };
21506    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21507        return false;
21508    }
21509    if assignment
21510        .child(1)
21511        .is_none_or(|operator| operator.kind() != "=")
21512    {
21513        return false;
21514    }
21515    let Some(call) = assignment.child_by_field_name("left") else {
21516        return false;
21517    };
21518    let Some(default_value) = assignment.child_by_field_name("right") else {
21519        return false;
21520    };
21521    let Some(function) = call.child_by_field_name("function") else {
21522        return false;
21523    };
21524    let Some(arguments) = call.child_by_field_name("arguments") else {
21525        return false;
21526    };
21527    if call.kind() != "call_expression"
21528        || function.kind() != "identifier"
21529        || arguments.kind() != "argument_list"
21530        || arguments.named_child_count() != 0
21531        || default_value.kind() != "identifier"
21532        || node_text(default_value, source) != "default"
21533        || node_text(function, source) != class_name
21534    {
21535        return false;
21536    }
21537    members.iter().any(|member| {
21538        if same_node(*member, node) {
21539            return false;
21540        }
21541        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21542    })
21543}
21544
21545/// Return the constructor name from one direct class-member-shaped reparse
21546/// item. The function-declarator in an `ERROR` is admitted only through the
21547/// existing ERROR-only member proof; clean function definitions must carry no
21548/// return type and must retain either a body or ABI 15's `default_method_clause`.
21549fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21550    let function_declarator = match node.kind() {
21551        "function_definition"
21552            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21553        {
21554            let has_body = node
21555                .child_by_field_name("body")
21556                .is_some_and(|body| body.kind() == "compound_statement");
21557            let has_default_method_clause = node
21558                .named_children(&mut node.walk())
21559                .any(|child| child.kind() == "default_method_clause");
21560            if !has_body && !has_default_method_clause {
21561                return None;
21562            }
21563            node.child_by_field_name("declarator")
21564                .and_then(extract_function_declarator)
21565        }
21566        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21567            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21568            let mut candidate = None;
21569            while let Some(current) = stack.pop() {
21570                if current.kind() == "function_declarator" {
21571                    if candidate.is_some() {
21572                        return None;
21573                    }
21574                    candidate = Some(current);
21575                    continue;
21576                }
21577                if current.kind() == "ERROR" {
21578                    let mut cursor = current.walk();
21579                    stack.extend(current.named_children(&mut cursor));
21580                }
21581            }
21582            candidate
21583        }
21584        _ => None,
21585    }?;
21586    cpp_function_declarator_name_node(function_declarator)
21587        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
21588}
21589
21590fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
21591    cpp_reparsed_members_are_indexable_for_class(root, source, None)
21592}
21593
21594fn cpp_reparsed_members_are_indexable_for_class(
21595    root: Node<'_>,
21596    source: &str,
21597    class_name: Option<&str>,
21598) -> bool {
21599    let mut saw_member = false;
21600    let mut regions = Vec::new();
21601    let mut region_tree: Option<Tree> = None;
21602    loop {
21603        let root = region_tree.as_ref().map_or(root, Tree::root_node);
21604        let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
21605        let mut index = 0;
21606        while index < children.len() {
21607            let child = children[index];
21608            if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
21609                saw_member = true;
21610                index += 3;
21611                continue;
21612            }
21613            if let Some(recovered) = fragmented_class_body(child, source) {
21614                let Some(tree) = cpp_reparse_fragmented_class_body(
21615                    source,
21616                    recovered.body.reparse_start,
21617                    recovered.body.reparse_end,
21618                ) else {
21619                    return false;
21620                };
21621                if !cpp_reparsed_members_are_indexable_for_class(
21622                    tree.root_node(),
21623                    source,
21624                    Some(recovered.name.as_str()),
21625                ) {
21626                    return false;
21627                }
21628                saw_member = true;
21629                index += 1;
21630                while index < children.len()
21631                    && children[index].end_byte() <= recovered.body.class_range.end_byte
21632                {
21633                    index += 1;
21634                }
21635                continue;
21636            }
21637            match child.kind() {
21638                "comment" => {}
21639                "labeled_statement" => {
21640                    let Some(label) = child.child_by_field_name("label") else {
21641                        return false;
21642                    };
21643                    if !matches!(node_text(label, source), "public" | "protected" | "private") {
21644                        return false;
21645                    }
21646                    let payload = child
21647                        .named_children(&mut child.walk())
21648                        .filter(|node| node.id() != label.id() && node.kind() != "comment")
21649                        .collect::<Vec<_>>();
21650                    if payload.is_empty() {
21651                        return false;
21652                    }
21653                    // The statement-context grammar splits these constructors
21654                    // across an ERROR and initializer-looking declarations.
21655                    if class_name.is_some_and(|name| {
21656                        cpp_reparsed_synthetic_initializer_constructor(
21657                            child,
21658                            name,
21659                            source,
21660                            child.end_byte(),
21661                        )
21662                        .is_some()
21663                            || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
21664                    }) {
21665                        saw_member = true;
21666                        index += 1;
21667                        continue;
21668                    }
21669                    // A destructor in statement context is a unary '~' call,
21670                    // with a possible stranded virtual keyword ahead of it.
21671                    let destructor = match payload.as_slice() {
21672                        [expression] => Some(*expression),
21673                        [specifier, expression]
21674                            if specifier.kind() == "ERROR"
21675                                && specifier.child_count() == 1
21676                                && specifier
21677                                    .child(0)
21678                                    .is_some_and(|keyword| keyword.kind() == "virtual") =>
21679                        {
21680                            Some(*expression)
21681                        }
21682                        _ => None,
21683                    };
21684                    if destructor.is_some_and(|expression| {
21685                        expression.kind() == "expression_statement"
21686                            && !expression.has_error()
21687                            && expression.named_child(0).is_some_and(|unary| {
21688                                unary.kind() == "unary_expression"
21689                                    && unary
21690                                        .child_by_field_name("operator")
21691                                        .is_some_and(|operator| operator.kind() == "~")
21692                                    && unary.child_by_field_name("argument").is_some_and(|call| {
21693                                        call.kind() == "call_expression"
21694                                            && call.child_by_field_name("function").is_some_and(
21695                                                |name| {
21696                                                    name.kind() == "identifier"
21697                                                        && class_name.is_none_or(|owner| {
21698                                                            node_text(name, source) == owner
21699                                                        })
21700                                                },
21701                                            )
21702                                            && call.child_by_field_name("arguments").is_some_and(
21703                                                |arguments| arguments.named_child_count() == 0,
21704                                            )
21705                                    })
21706                            })
21707                    }) {
21708                        saw_member = true;
21709                    } else {
21710                        // A label puts its payload in statement context, where
21711                        // static assertions look like calls and alias templates
21712                        // can become ERROR nodes. Reparse after the colon as
21713                        // declarations and apply this same gate to that region.
21714                        let colon = child
21715                            .children(&mut child.walk())
21716                            .find(|node| node.kind() == ":")
21717                            .expect("an access label has a colon");
21718                        regions.push(colon.end_byte()..child.end_byte());
21719                    }
21720                }
21721                "function_definition" => {
21722                    if child.has_error()
21723                        && !cpp_reparsed_member_function_is_indexable(child, source)
21724                        && cpp_sentinel_macro_region(child, source).is_none()
21725                    {
21726                        return false;
21727                    }
21728                    saw_member = true;
21729                }
21730                // The attribute a string-argument macro leaves as a call statement
21731                // of its own. It declares nothing; the member it decorated is the
21732                // sibling after it, checked on its own turn.
21733                "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
21734                "expression_statement"
21735                    if cpp_reparsed_defaulted_constructor_is_indexable(
21736                        child, &children, class_name, source,
21737                    ) =>
21738                {
21739                    saw_member = true;
21740                }
21741                "expression_statement"
21742                    if class_name.is_some_and(|class_name| {
21743                        cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
21744                    }) =>
21745                {
21746                    saw_member = true;
21747                }
21748                "ERROR"
21749                    if (cpp_reparsed_member_error_is_indexable(child)
21750                        || cpp_reparsed_adjacent_copy_control_error(child, source)
21751                        || cpp_reparsed_stranded_member_error(child, source))
21752                        && (child
21753                        .next_named_sibling()
21754                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
21755                        || cpp_reparsed_member_error_with_preprocessed_body(child)
21756                        // The macro-declaration grammar folds the member after
21757                        // the copy-control pair into the same ERROR and leaves
21758                        // its body as the following sibling.
21759                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
21760                            && child
21761                                .next_named_sibling()
21762                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
21763                {
21764                    saw_member = true;
21765                }
21766                "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
21767                    saw_member = true;
21768                }
21769                "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
21770                    saw_member = true;
21771                }
21772                "expression_statement"
21773                    if cpp_is_stray_semicolon(child, source)
21774                        && child.prev_named_sibling().is_some_and(|error| {
21775                            cpp_reparsed_member_error_is_indexable(error)
21776                                || cpp_reparsed_adjacent_copy_control_error(error, source)
21777                                || cpp_reparsed_stranded_member_error(error, source)
21778                        }) =>
21779                {
21780                    saw_member = true;
21781                }
21782                "compound_statement"
21783                    if cpp_reparsed_constructor_body_is_indexable(child, source)
21784                        || cpp_reparsed_attribute_requires_body(child, source)
21785                        || child.prev_named_sibling().is_some_and(|error| {
21786                            cpp_reparsed_adjacent_copy_control_error(error, source)
21787                        }) =>
21788                {
21789                    saw_member = true;
21790                }
21791                // A copy-control declaration the macro-declaration grammar reads
21792                // as a function-like macro wrapping a type: `XMLElement( const
21793                // XMLElement& )` in a statement-context reparse is a
21794                // `macro_type_specifier`, admitted only where the parse would
21795                // otherwise contain an error. The live `;` beside it is the
21796                // declaration's own terminator, the same proof the ERROR
21797                // envelope's stray semicolon carried.
21798                "macro_type_specifier"
21799                    if child
21800                        .child_by_field_name("name")
21801                        .is_some_and(|name| name.kind() == "identifier")
21802                        && child
21803                            .child_by_field_name("type")
21804                            .is_some_and(|type_node| type_node.kind() == "type_descriptor")
21805                        && child.next_sibling().is_some_and(|sibling| {
21806                            sibling.kind() == ";" && !sibling.is_missing()
21807                        }) =>
21808                {
21809                    saw_member = true;
21810                }
21811                kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
21812                _ => return false,
21813            }
21814            index += 1;
21815        }
21816        let Some(region) = regions.pop() else {
21817            return saw_member;
21818        };
21819        let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
21820            return false;
21821        };
21822        region_tree = Some(tree);
21823    }
21824}
21825
21826/// Detect the malformed constructor shape that tree-sitter exposes as an
21827/// access-label statement followed by initializer-looking declarations. The
21828/// declarations are not class members: visiting their `location(loc)` and
21829/// `string(s)` function declarators would publish synthetic functions. The
21830/// export-class fallback keeps the original sibling nodes and therefore avoids
21831/// this parser artifact. The returned range identifies the real constructor
21832/// header, which can be reparsed independently as a structured declarator.
21833fn cpp_reparsed_synthetic_initializer_constructor_range(
21834    root: Node<'_>,
21835    class_name: &str,
21836    source: &str,
21837    constructor_end: usize,
21838) -> Option<std::ops::Range<usize>> {
21839    let mut stack = {
21840        let mut cursor = root.walk();
21841        root.named_children(&mut cursor).collect::<Vec<_>>()
21842    };
21843    while let Some(current) = stack.pop() {
21844        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
21845            current,
21846            class_name,
21847            source,
21848            constructor_end,
21849        ) {
21850            return Some(range);
21851        }
21852        if current.kind() == "ERROR" {
21853            let mut cursor = current.walk();
21854            stack.extend(current.named_children(&mut cursor));
21855        }
21856    }
21857    None
21858}
21859
21860/// Recover an inline constructor that a function-like export macro makes
21861/// tree-sitter merge with the following overload. In the reparsed class-body
21862/// region, the access label wraps one declaration whose ERROR contains the
21863/// constructor declarator and its base-initializer/body, while the declaration's
21864/// ordinary declarator is the following overload. Every boundary below comes
21865/// from that CST; no source syntax is reparsed by hand.
21866fn cpp_reparsed_merged_inline_constructor<'tree>(
21867    root: Node<'tree>,
21868    class_name: &str,
21869    source: &str,
21870) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
21871    let mut stack = vec![root];
21872    while let Some(current) = stack.pop() {
21873        if current.kind() != "labeled_statement" {
21874            let mut cursor = current.walk();
21875            stack.extend(current.named_children(&mut cursor));
21876            continue;
21877        }
21878        let Some(declaration) = current
21879            .named_children(&mut current.walk())
21880            .find(|child| child.kind() == "declaration")
21881        else {
21882            continue;
21883        };
21884        if declaration
21885            .child_by_field_name("type")
21886            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
21887        {
21888            continue;
21889        }
21890        let following = declaration
21891            .child_by_field_name("declarator")
21892            .and_then(extract_function_declarator)
21893            .and_then(cpp_function_declarator_name_node);
21894        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
21895            continue;
21896        }
21897        let mut declaration_cursor = declaration.walk();
21898        let Some(error) = declaration
21899            .named_children(&mut declaration_cursor)
21900            .find(|child| child.kind() == "ERROR")
21901        else {
21902            continue;
21903        };
21904        let mut error_cursor = error.walk();
21905        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
21906        let Some(constructor) = error_children.iter().copied().find(|child| {
21907            child.kind() == "function_declarator"
21908                && cpp_function_declarator_name_node(*child)
21909                    .is_some_and(|name| node_text(name, source).trim() == class_name)
21910        }) else {
21911            continue;
21912        };
21913        let Some(body) = error_children.iter().copied().find_map(|child| {
21914            (child.kind() == "init_declarator")
21915                .then(|| child.child_by_field_name("value"))
21916                .flatten()
21917                .filter(|value| value.kind() == "initializer_list")
21918        }) else {
21919            continue;
21920        };
21921        if constructor.end_byte() > body.start_byte() {
21922            continue;
21923        }
21924        return Some((constructor.start_byte()..body.end_byte(), body));
21925    }
21926    None
21927}
21928
21929fn cpp_reparsed_synthetic_initializer_constructor(
21930    node: Node<'_>,
21931    class_name: &str,
21932    source: &str,
21933    constructor_end: usize,
21934) -> Option<std::ops::Range<usize>> {
21935    if node.kind() != "labeled_statement" {
21936        return None;
21937    }
21938    let mut cursor = node.walk();
21939    let named = node
21940        .named_children(&mut cursor)
21941        .filter(|child| child.kind() != "comment")
21942        .collect::<Vec<_>>();
21943    let label = named.first()?;
21944    if label.kind() != "statement_identifier"
21945        || !matches!(
21946            node_text(*label, source).trim(),
21947            "public" | "private" | "protected"
21948        )
21949    {
21950        return None;
21951    }
21952    let call_error_index = named.iter().position(|child| {
21953        if child.kind() != "ERROR" {
21954            return false;
21955        }
21956        // The macro-declaration grammar keeps the constructor head's type as
21957        // the ERROR's own type field; the older grammar flattened the head
21958        // into a call expression inside it. ERROR nodes retain field labels
21959        // only by child index in runtime 0.25.
21960        let type_child = (0..child.child_count()).find_map(|index| {
21961            let candidate = child.child(index)?;
21962            (child.field_name_for_child(index as u32) == Some("type")
21963                && candidate.kind() == "type_identifier"
21964                && node_text(candidate, source).trim() == class_name)
21965                .then_some(candidate)
21966        });
21967        if type_child.is_some() {
21968            return true;
21969        }
21970        let mut stack = vec![*child];
21971        while let Some(current) = stack.pop() {
21972            if current.kind() == "call_expression"
21973                && current
21974                    .child_by_field_name("function")
21975                    .is_some_and(|function| {
21976                        function.kind() == "identifier"
21977                            && node_text(function, source).trim() == class_name
21978                    })
21979            {
21980                return true;
21981            }
21982            let mut cursor = current.walk();
21983            stack.extend(current.named_children(&mut cursor));
21984        }
21985        false
21986    })?;
21987    let constructor_start = {
21988        let head = named[call_error_index];
21989        let type_child = (0..head.child_count()).find_map(|index| {
21990            let candidate = head.child(index)?;
21991            (head.field_name_for_child(index as u32) == Some("type")
21992                && candidate.kind() == "type_identifier"
21993                && node_text(candidate, source).trim() == class_name)
21994                .then_some(candidate)
21995        });
21996        if let Some(type_node) = type_child {
21997            type_node.start_byte()
21998        } else {
21999            let mut stack = vec![head];
22000            let mut found = None;
22001            while let Some(current) = stack.pop() {
22002                if current.kind() == "call_expression"
22003                    && current
22004                        .child_by_field_name("function")
22005                        .is_some_and(|function| {
22006                            function.kind() == "identifier"
22007                                && node_text(function, source).trim() == class_name
22008                        })
22009                {
22010                    found = Some(current);
22011                    break;
22012                }
22013                let mut cursor = current.walk();
22014                stack.extend(current.named_children(&mut cursor));
22015            }
22016            found?.start_byte()
22017        }
22018    };
22019    named.iter().skip(call_error_index + 1).find(|child| {
22020        child.kind() == "declaration" && child.has_error() && {
22021            let mut cursor = child.walk();
22022            child.named_children(&mut cursor).any(|declarator| {
22023                declarator.kind() == "init_declarator"
22024                    && declarator
22025                        .child_by_field_name("declarator")
22026                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
22027                    && declarator
22028                        .child_by_field_name("value")
22029                        .is_some_and(|value| value.kind() == "initializer_list")
22030            })
22031        }
22032    })?;
22033    Some(constructor_start..constructor_end)
22034}
22035
22036fn cpp_reparsed_exact_constructor_declarator<'tree>(
22037    root: Node<'tree>,
22038    start: usize,
22039    class_name: &str,
22040    source: &str,
22041) -> Option<Node<'tree>> {
22042    let mut candidate = None;
22043    let mut stack = vec![root];
22044    while let Some(current) = stack.pop() {
22045        if current.kind() == "function_declarator"
22046            && current.start_byte() == start
22047            && cpp_function_declarator_name_node(current)
22048                .is_some_and(|name| node_text(name, source).trim() == class_name)
22049        {
22050            if candidate.is_some() {
22051                return None;
22052            }
22053            candidate = Some(current);
22054            continue;
22055        }
22056        let mut cursor = current.walk();
22057        stack.extend(current.named_children(&mut cursor));
22058    }
22059    candidate
22060}
22061
22062fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22063    matches!(
22064        kind,
22065        "namespace_definition"
22066            | "class_specifier"
22067            | "struct_specifier"
22068            | "union_specifier"
22069            | "enum_specifier"
22070            | "function_definition"
22071            | "template_declaration"
22072            | "declaration"
22073            | "field_declaration"
22074            | "alias_declaration"
22075            | "static_assert_declaration"
22076            | "type_definition"
22077            | "using_declaration"
22078            | "linkage_specification"
22079            | "preproc_def"
22080            | "preproc_function_def"
22081            | "preproc_include"
22082            | "preproc_if"
22083            | "preproc_ifdef"
22084            | "preproc_call"
22085    )
22086}
22087
22088fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> [u8; 32] {
22089    use brokk_bifrost_core::analyzer::tree_walk::{
22090        canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22091    };
22092    let mut tokens = Vec::new();
22093    if let Some(parameters) = declarator.child_by_field_name("parameters") {
22094        for slot in cpp_callable_parameter_slots(parameters, source) {
22095            match slot {
22096                CppParameterSlot::Declared { parameter, .. } => {
22097                    tokens.extend(overload_parameter_tokens(
22098                        parameter.child_by_field_name("type"),
22099                        source,
22100                    ));
22101                    let mut cursor = parameter.walk();
22102                    for qualifier in parameter.named_children(&mut cursor) {
22103                        if qualifier.kind() == "type_qualifier" {
22104                            tokens.extend(canonical_type_tokens(qualifier, source));
22105                        }
22106                    }
22107                    // Pointer/reference/array/function declarators carry type
22108                    // structure outside the base type. Exclude only the name.
22109                    if let Some(declarator) = parameter.child_by_field_name("declarator") {
22110                        let name = crate::structural::declarator_name_node(declarator);
22111                        let mut pending = vec![(declarator, name)];
22112                        while let Some((node, excluded_name)) = pending.pop() {
22113                            if Some(node) == excluded_name {
22114                                continue;
22115                            }
22116                            if node.child_count() == 0 {
22117                                tokens.extend(canonical_type_tokens(node, source));
22118                            } else {
22119                                let parameter = matches!(
22120                                    node.kind(),
22121                                    "parameter_declaration" | "optional_parameter_declaration"
22122                                );
22123                                let excluded_name = if parameter {
22124                                    node.child_by_field_name("declarator")
22125                                        .and_then(crate::structural::declarator_name_node)
22126                                } else {
22127                                    excluded_name
22128                                };
22129                                let default = parameter
22130                                    .then(|| node.child_by_field_name("default_value"))
22131                                    .flatten();
22132                                for index in (0..node.child_count()).rev() {
22133                                    let child = node.child(index).expect("child");
22134                                    if Some(child) != default {
22135                                        pending.push((child, excluded_name));
22136                                    }
22137                                }
22138                            }
22139                        }
22140                    }
22141                }
22142                CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22143                CppParameterSlot::OptionalVoid => {}
22144            }
22145        }
22146    }
22147    let mut cursor = declarator.walk();
22148    let mut qualifiers = declarator
22149        .named_children(&mut cursor)
22150        .filter(|node| node.kind() == "type_qualifier")
22151        .map(|node| canonical_type_tokens(node, source))
22152        .collect::<Vec<_>>();
22153    qualifiers.sort();
22154    for qualifier in qualifiers {
22155        tokens.push("<member-qualifier>".to_owned());
22156        tokens.extend(qualifier);
22157    }
22158    for qualifier in declarator.named_children(&mut cursor) {
22159        if qualifier.kind() == "ref_qualifier" {
22160            tokens.push("<member-qualifier>".to_owned());
22161            tokens.extend(canonical_type_tokens(qualifier, source));
22162        }
22163    }
22164    overload_discriminator(tokens)
22165}
22166
22167#[cfg(test)]
22168mod tests {
22169
22170    #[test]
22171    fn recovered_callable_grammar_and_original_bytes() {
22172        use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22173
22174        let parsed = tree(SOURCE);
22175        // These are the current grammar's contracts, including the macro_modifier
22176        // support that has replaced some older ERROR/argument_list recoveries.
22177        // Each row selects a real source region and independently pins its fields
22178        // and the tokens that must survive a synthetic class-context reparse.
22179        let cases = [
22180            (
22181                "expression_statement",
22182                58..72,
22183                &[][..],
22184                &[(58..64, "Widget"), (65..70, "value")][..],
22185            ),
22186            (
22187                "field_declaration",
22188                92..145,
22189                &[
22190                    ("type", "qualified_identifier", 111..122),
22191                    ("declarator", "function_declarator", 123..144),
22192                ][..],
22193                &[
22194                    (123..131, "toString"),
22195                    (132..137, "Yield"),
22196                    (138..143, "yield"),
22197                ][..],
22198            ),
22199            (
22200                "function_definition",
22201                147..191,
22202                &[
22203                    ("type", "qualified_identifier", 151..161),
22204                    ("declarator", "parenthesized_declarator", 161..174),
22205                    ("body", "compound_statement", 189..191),
22206                ][..],
22207                &[
22208                    (155..161, "Widget"),
22209                    (162..167, "Value"),
22210                    (168..173, "value"),
22211                ][..],
22212            ),
22213            (
22214                "field_declaration",
22215                193..296,
22216                &[
22217                    ("type", "macro_type_specifier", 193..222),
22218                    ("declarator", "macro_modifier_declarator", 223..263),
22219                    ("default_value", "initializer_list", 291..293),
22220                ][..],
22221                &[
22222                    (233..239, "Widget"),
22223                    (240..248, "grow_fun"),
22224                    (249..253, "grow"),
22225                ][..],
22226            ),
22227            (
22228                "call_expression",
22229                58..71,
22230                &[
22231                    ("function", "identifier", 58..64),
22232                    ("arguments", "argument_list", 64..71),
22233                ][..],
22234                &[][..],
22235            ),
22236            (
22237                "function_declarator",
22238                123..144,
22239                &[
22240                    ("declarator", "field_identifier", 123..131),
22241                    ("parameters", "parameter_list", 131..144),
22242                ][..],
22243                &[][..],
22244            ),
22245            (
22246                "function_declarator",
22247                233..263,
22248                &[
22249                    ("declarator", "field_identifier", 233..239),
22250                    ("parameters", "parameter_list", 239..254),
22251                ][..],
22252                &[][..],
22253            ),
22254        ];
22255        for (kind, span, fields, tokens) in cases {
22256            let node = at(parsed.root_node(), kind, span.clone());
22257            for (name, child_kind, child_span) in fields {
22258                field(node, name, child_kind, child_span.clone());
22259            }
22260            if tokens.is_empty() {
22261                continue;
22262            }
22263            // Match visit_bare_constructor_expression's documented class context.
22264            // The initializer-list body bounds the initialized-constructor region;
22265            // its recovery field also swallowed the original class close.
22266            let end = node
22267                .child_by_field_name("default_value")
22268                .map_or(span.end, |body| body.end_byte());
22269            const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22270            let parsed = crate::graph::syntax::parse_sentinel(
22271                PREFIX,
22272                crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22273                " };",
22274            );
22275            let (synthetic, reparsed) = (parsed.source, parsed.tree);
22276            for (original_span, text) in tokens {
22277                assert_eq!(&SOURCE[original_span.clone()], *text);
22278                let translated = PREFIX.len() + original_span.start - span.start
22279                    ..PREFIX.len() + original_span.end - span.start;
22280                let leaves: Vec<_> = nodes(reparsed.root_node())
22281                    .into_iter()
22282                    .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22283                    .collect();
22284                assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22285                let token = leaves[0];
22286                assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22287                assert_eq!(
22288                    token.start_byte() - PREFIX.len() + span.start
22289                        ..token.end_byte() - PREFIX.len() + span.start,
22290                    original_span.clone()
22291                );
22292                assert!(SOURCE.is_char_boundary(original_span.start));
22293                assert!(SOURCE.is_char_boundary(original_span.end));
22294            }
22295        }
22296    }
22297
22298    use super::*;
22299    use crate::adapter::parse_cpp_file;
22300    use brokk_bifrost_core::analyzer::parsed_file::{
22301        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22302    };
22303    use brokk_bifrost_test_macros::hardwon;
22304    use std::fmt::Write;
22305
22306    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22307        let tree = crate::diagnostics::parse_cpp_tree(source);
22308        let file = ProjectFile::new(std::env::temp_dir(), name);
22309        parse_cpp_file(&file, source, &tree)
22310    }
22311
22312    #[test]
22313    #[hardwon(
22314        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22315    )]
22316    fn comment_only_enums_have_no_enumerators() {
22317        let parsed =
22318            parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22319        let names: Vec<_> = parsed
22320            .declarations()
22321            .iter()
22322            .map(|unit| unit.fq_name())
22323            .collect();
22324        assert_eq!(names, ["Empty"]);
22325    }
22326
22327    #[test]
22328    #[hardwon(
22329        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22330    )]
22331    fn quicksilver_gnu_attribute_orders() {
22332        for attributes in [
22333            "deprecated, warn_unused_result",
22334            "warn_unused_result, deprecated",
22335        ] {
22336            let source = format!("int value() __attribute__(({attributes}));");
22337            let tree = crate::diagnostics::parse_cpp_tree(&source);
22338            assert!(
22339                !tree.root_node().has_error(),
22340                "{}",
22341                tree.root_node().to_sexp()
22342            );
22343            let declaration = tree.root_node().named_child(0).unwrap();
22344            assert!(
22345                cpp_declaration_has_native_must_use_attribute(declaration, &source),
22346                "{source}: {}",
22347                declaration.to_sexp()
22348            );
22349        }
22350    }
22351
22352    #[test]
22353    #[hardwon(
22354        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22355    )]
22356    fn fragmented_brace_matching_ignores_literal_tokens() {
22357        // Isolate the brace proof from export-head recognition: the damaged
22358        // field list supplies the same container boundary used by recovery.
22359        let source =
22360            r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22361        let tree = crate::diagnostics::parse_cpp_tree(source);
22362        let tokens = crate::grammar_test_support::nodes(tree.root_node());
22363        assert!(
22364            tokens
22365                .iter()
22366                .any(|node| node.kind() == "raw_string_literal")
22367        );
22368        let body = tokens
22369            .iter()
22370            .find(|node| node.kind() == "field_declaration_list")
22371            .unwrap();
22372        let opening = body.child(0).unwrap();
22373        let head = body.named_child(0).unwrap();
22374        assert_eq!(opening.kind(), "{");
22375        assert!(body.has_error());
22376        assert_eq!(
22377            fragmented_container_close_class_body(head, opening, source)
22378                .map(|body| body.reparse_end),
22379            source.rfind('}')
22380        );
22381    }
22382
22383    #[test]
22384    #[hardwon(
22385        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22386    )]
22387    fn quicksilver_uppercase_attributed_classes() {
22388        for (name, body) in [
22389            ("Box", "{ int value; }"),
22390            ("BOX", "{ int value; }"),
22391            ("API", ""),
22392        ] {
22393            let source = format!("class [[deprecated]] {name} {body};");
22394            let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22395            let names = parsed
22396                .declarations()
22397                .iter()
22398                .filter(|unit| unit.is_class())
22399                .map(|unit| unit.identifier().to_owned())
22400                .collect::<Vec<_>>();
22401            assert_eq!(names, [name], "{source}");
22402        }
22403    }
22404
22405    #[test]
22406    #[hardwon(
22407        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22408    )]
22409    fn quicksilver_fragmented_export_uppercase_classes() {
22410        for name in ["Box", "BOX"] {
22411            let source = r#"
22412namespace api {
22413
22414/**
22415* Doc comment
22416*/
22417class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22418   public:
22419      /**
22420      * Construct from a point.
22421      */
22422      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22423
22424#if defined(PROJECT_HAS_LEGACY_POINT)
22425      /**
22426      * Construct from a legacy point.
22427      */
22428      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22429#endif
22430
22431      std::string algo_name() const override;
22432
22433      AlgorithmIdentifier algorithm_identifier() const override;
22434};
22435
22436}
22437"#
22438            .replace("VirtualBased", name);
22439            // Positional recovery once named this class. Since
22440            // brokk-tree-sitter-cpp 0.24.7 the grammar reads the head, and an
22441            // uppercase name must survive that reading too.
22442            let tree = crate::grammar_test_support::tree(&source);
22443            crate::grammar_test_support::assert_function_like_export_class_head(
22444                &tree, &source, name,
22445            );
22446            let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22447            let explicit = parse_cpp_declarations(
22448                &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22449                "virtual-base.hpp",
22450            );
22451            assert_eq!(
22452                declaration_expansion_inventory(&parsed),
22453                declaration_expansion_inventory(&explicit),
22454                "{name}"
22455            );
22456            let declarations = parsed.declarations();
22457            let class = declarations
22458                .iter()
22459                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22460                .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22461            assert_eq!(
22462                parsed.raw_supertypes.get(class),
22463                Some(&vec!["BaseKey".to_string()]),
22464                "{name}"
22465            );
22466            assert!(
22467                declarations.iter().any(|unit| {
22468                    unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22469                }),
22470                "{name}: the class must own its members: {declarations:#?}"
22471            );
22472        }
22473    }
22474
22475    #[test]
22476    #[hardwon(
22477        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22478    )]
22479    fn quicksilver_constructor_search_skips_other_labels() {
22480        let source = "public:\n explicit Lookup_Error(std::string_view err) : Exception(err) {}\n Lookup_Error(std::string_view type, std::string_view algo);\nprivate: do_work();\n";
22481        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22482        let (range, body) =
22483            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22484                .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22485        assert_eq!(
22486            &source[range],
22487            "Lookup_Error(std::string_view err) : Exception(err) {}"
22488        );
22489        assert_eq!(node_text(body, source), "{}");
22490    }
22491
22492    #[test]
22493    #[hardwon(
22494        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
22495    )]
22496    fn quicksilver_member_gate_checks_label_payload() {
22497        for (source, expected) in [
22498            ("public: int value;", true),
22499            (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
22500            (
22501                "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
22502                true,
22503            ),
22504            ("public: do_work();", false),
22505            ("ordinary: int value;", false),
22506        ] {
22507            let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22508            assert_eq!(
22509                cpp_reparsed_members_are_indexable(tree.root_node(), source),
22510                expected,
22511                "{source}: {}",
22512                tree.root_node().to_sexp()
22513            );
22514        }
22515    }
22516
22517    #[test]
22518    #[hardwon(
22519        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
22520    )]
22521    fn quicksilver_sentinel_uppercase_classes() {
22522        for name in ["Box", "BOX"] {
22523            let source = format!(
22524                "namespace absl {{\nABSL_NAMESPACE_BEGIN namespace container_internal {{\ntemplate <typename T> class {name} {{\npublic:\nusing value_type = T;\nT operator->() const {{ return &operator*(); }}\nusing alias = value_type;\n}};\n}}\n}}\n"
22525            );
22526            let tree = crate::diagnostics::parse_cpp_tree(&source);
22527            let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
22528            assert!(
22529                recovered
22530                    .iter()
22531                    .any(|class| class.scope_components == ["absl", "container_internal", name]),
22532                "{source}: {recovered:#?}"
22533            );
22534        }
22535    }
22536
22537    #[test]
22538    fn native_must_use_attribute_nodes_are_structural() {
22539        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
22540        let tree = crate::diagnostics::parse_cpp_tree(source);
22541        let root = tree.root_node();
22542        let mut declarations = Vec::new();
22543        walk_named_tree_preorder(root, true, |node| {
22544            if matches!(
22545                node.kind(),
22546                "declaration" | "function_definition" | "field_declaration"
22547            ) {
22548                declarations.push(node);
22549            }
22550            WalkControl::Continue
22551        });
22552        assert_eq!(
22553            declarations
22554                .into_iter()
22555                .map(|declaration| {
22556                    cpp_declaration_has_native_must_use_attribute(declaration, source)
22557                })
22558                .collect::<Vec<_>>(),
22559            vec![true, false, true]
22560        );
22561        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
22562        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
22563        let mut enum_specifier = None;
22564        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
22565            if node.kind() == "enum_specifier" {
22566                enum_specifier = Some(node);
22567                WalkControl::Break
22568            } else {
22569                WalkControl::Continue
22570            }
22571        });
22572        assert!(cpp_declaration_has_native_must_use_attribute(
22573            enum_specifier.expect("enum declaration"),
22574            enum_source
22575        ));
22576    }
22577
22578    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
22579        let mut rows = parsed
22580            .signature_metadata
22581            .iter()
22582            .flat_map(|(unit, entries)| {
22583                entries.iter().map(move |metadata| {
22584                    assert!(
22585                        metadata.callable_modifiers_recorded(),
22586                        "{} must record that the walk read its declaration shape",
22587                        unit.fq_name()
22588                    );
22589                    format!(
22590                        "{} decl_only={} static={} constructor={} visibility={:?}",
22591                        unit.fq_name(),
22592                        metadata.is_declaration_only(),
22593                        metadata.callable_is_static(),
22594                        metadata.callable_is_constructor(),
22595                        metadata
22596                            .callable_declared_visibility()
22597                            .expect("a recorded modifier set states a visibility"),
22598                    )
22599                })
22600            })
22601            .collect::<Vec<_>>();
22602        rows.sort();
22603        rows
22604    }
22605
22606    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
22607    /// exactly the spelling it was given, with the nested-class `$` recorded as
22608    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
22609    #[test]
22610    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
22611    fn rendered_name_fq_round_trips_nested_class_spellings() {
22612        assert!(cpp_rendered_name_fq("").is_empty());
22613        for rendered in [
22614            "Only",
22615            "Top$Only",
22616            "ns.Outer$Inner",
22617            "Top$Only.b",
22618            "ns.Outer$Inner.a",
22619            "Buck$Stop",
22620        ] {
22621            let fq = cpp_rendered_name_fq(rendered);
22622            assert_eq!(
22623                fq.display_native(Language::Cpp, segment_interner()),
22624                rendered,
22625                "native round trip for {rendered}"
22626            );
22627        }
22628        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
22629            .segments()
22630            .iter()
22631            .map(|segment| segment_interner().resolve(*segment).1)
22632            .collect();
22633        assert_eq!(
22634            kinds,
22635            [
22636                SegmentKind::Unknown,
22637                SegmentKind::Unknown,
22638                SegmentKind::Nested
22639            ]
22640        );
22641    }
22642
22643    /// #3631: a `::`-only source spelling must name the nested `$` identity
22644    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
22645    /// the single rendered-name parse.
22646    #[test]
22647    #[hardwon(
22648        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
22649    )]
22650    fn colon_source_aliases_name_nested_and_namespace_identities() {
22651        let native = |rendered: &str| {
22652            cpp_colon_source_aliases(rendered)
22653                .into_iter()
22654                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
22655                .collect::<Vec<_>>()
22656        };
22657        let top = native("Top::Only");
22658        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
22659        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
22660        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
22661        let nested = native("ns::Outer::Inner");
22662        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
22663        assert!(
22664            nested.contains(&"ns::Outer.Inner".to_string()),
22665            "{nested:?}"
22666        );
22667        let member = native("Top::Only::b");
22668        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
22669        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
22670        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
22671        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
22672        assert!(cpp_colon_source_aliases("Only").is_empty());
22673        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
22674            fq.segments()
22675                .iter()
22676                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
22677        }));
22678    }
22679
22680    #[test]
22681    #[hardwon(
22682        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
22683    )]
22684    fn displaced_terminator_ignores_nested_initializer_endif() {
22685        let source = "#ifdef ENABLE_ITEMS\n\
22686struct Item { int reg; };\n\
22687static const struct Item items[] = {{1}};\n\
22688static const struct Item extras[] = {\n\
22689  {0},\n\
22690#ifndef REDUCED\n\
22691  {1},\n\
22692#endif\n\
22693};\n\
22694int read_item(int i) { return items[i].reg; }\n\
22695#endif\n";
22696        let mut parser = tree_sitter::Parser::new();
22697        parser
22698            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22699            .expect("C++ grammar");
22700        let tree = parser.parse(source, None).expect("fixture tree");
22701        let conditional = tree
22702            .root_node()
22703            .named_child(0)
22704            .filter(|node| node.kind() == "preproc_ifdef")
22705            .expect("outer conditional");
22706
22707        assert!(conditional.has_error());
22708        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
22709        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
22710    }
22711
22712    #[test]
22713    #[hardwon(
22714        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22715    )]
22716    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
22717        let source = "struct Image { PyObject_HEAD Imaging image; };";
22718        let parsed = parse_cpp_declarations(source, "image.h");
22719        let names = parsed
22720            .declarations()
22721            .iter()
22722            .map(|unit| unit.fq_name())
22723            .collect::<Vec<_>>();
22724
22725        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
22726        assert!(
22727            names.iter().all(|name| name != "Image.Imaging"),
22728            "the pseudo-declarator must not become a field: {names:#?}"
22729        );
22730
22731        let pointer = parse_cpp_declarations(
22732            "struct Image { PyObject_HEAD Imaging *image; };",
22733            "image-pointer.h",
22734        );
22735        let pointer_names = pointer
22736            .declarations()
22737            .iter()
22738            .map(|unit| unit.fq_name())
22739            .collect::<Vec<_>>();
22740        assert!(
22741            pointer_names.iter().any(|name| name == "Image.image"),
22742            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
22743        );
22744        assert!(
22745            pointer_names.iter().all(|name| name != "Image.Imaging"),
22746            "the pointer recovery error must not become a field: {pointer_names:#?}"
22747        );
22748
22749        let unrelated_macro = parse_cpp_declarations(
22750            "struct Image { OTHER_HEAD Imaging other; };",
22751            "image-near-miss.h",
22752        );
22753        let unrelated_names = unrelated_macro
22754            .declarations()
22755            .iter()
22756            .map(|unit| unit.fq_name())
22757            .collect::<Vec<_>>();
22758        assert!(
22759            unrelated_names.iter().any(|name| name == "Image.other"),
22760            "an object-like macro in specifier position is a `macro_modifier`, so the \
22761             member is the structured best-effort reading, the same one the \
22762             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
22763        );
22764        assert!(
22765            unrelated_names
22766                .iter()
22767                .all(|name| name != "Image.OTHER_HEAD"),
22768            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
22769        );
22770    }
22771
22772    #[test]
22773    #[hardwon(
22774        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
22775    )]
22776    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
22777        let source = r#"namespace testing {
22778namespace internal {
22779    namespace detail {
22780        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
22781        public:
22782            int value() const { return count_ + 1; }
22783        private:
22784            int count_;
22785        };
22786        class GTEST_API_ [[nodiscard]] OtherReporter {
22787        public:
22788            int value() const { return count_ + 2; }
22789        private:
22790            int count_;
22791        };
22792    }
22793
22794    template <typename T>
22795    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
22796
22797    class TailReporter {};
22798}
22799}
22800"#;
22801        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
22802        let declarations = parsed.declarations();
22803        let mut parser = tree_sitter::Parser::new();
22804        parser
22805            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22806            .unwrap();
22807        let tree = parser.parse(source, None).unwrap();
22808        let tail_start = source.find("TailReporter").expect("tail class");
22809        let tail_node = tree
22810            .root_node()
22811            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
22812            .expect("tail class AST node");
22813        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22814        assert!(
22815            tree.root_node().has_error(),
22816            "the malformed class must exercise recovery"
22817        );
22818        assert!(index.region_at(tail_start).is_some());
22819        assert_eq!(
22820            index.enclosing_namespace_components(tail_node, source),
22821            ["testing", "internal"]
22822        );
22823        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
22824        let mut recovered_parsed = ParsedFile::new(String::new());
22825        let class_unit = CodeUnit::new_fq(
22826            file.clone(),
22827            CodeUnitType::Class,
22828            "testing",
22829            "ScopedFakeTestPartResultReporter",
22830            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
22831        );
22832        let scope = ScopeInfo {
22833            package_name: "testing".to_string(),
22834            module: None,
22835            class_unit: Some(class_unit),
22836            template_signature: Some("<typename T>".to_string()),
22837            template_metadata: Some(CppTemplateMetadata {
22838                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
22839                primary_fq_name: String::new(),
22840                parameters: Vec::new(),
22841                specialization_arguments: Vec::new(),
22842                alias_target: None,
22843            }),
22844            declarations_are_fields: true,
22845            recovered_specialization_member_scope: true,
22846            visible_using_namespaces: Vec::new(),
22847            local_block: None,
22848            visible_using_declarations: Arc::default(),
22849        };
22850        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22851        let properties = RefCell::new(HashMap::default());
22852        let dependencies =
22853            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22854        let ancestry = ParentIndex::new(tree.root_node());
22855        let mut visitor = CppVisitor {
22856            file: &file,
22857            source,
22858            parsed: &mut recovered_parsed,
22859            source_collector: &collector,
22860            source_properties: &properties,
22861            source_dependencies: &dependencies,
22862            primary_ancestry: &ancestry,
22863            primary_root_id: tree.root_node().id(),
22864            source_links: HashMap::default(),
22865            source_declarations: HashSet::default(),
22866            preprocessor_context_cache: Default::default(),
22867            active_source_context: None,
22868            c_tag_semantics: false,
22869            c_tag_scope_witness: false,
22870            recovered_class_sibling_scopes: HashMap::default(),
22871            consumed_fragment_regions: Vec::new(),
22872            orphaned_namespaces: &index,
22873            partitioned_regions: Vec::new(),
22874            namespace_forward_scans: HashMap::default(),
22875            recovery_captures: Vec::new(),
22876            object_macro_fields: HashMap::default(),
22877            ambiguous_object_macro_fields: HashSet::default(),
22878        };
22879        let recovered = visitor
22880            .recovered_namespace_scope(tail_node, &scope)
22881            .expect("the tail must use the stolen namespace scope");
22882        assert_eq!(recovered.package_name, "testing::internal");
22883        assert!(
22884            recovered.class_unit.is_none(),
22885            "recovered namespace declarations cannot retain the malformed class owner"
22886        );
22887        assert!(recovered.template_signature.is_none());
22888        assert!(recovered.template_metadata.is_none());
22889        assert!(!recovered.declarations_are_fields);
22890        assert!(!recovered.recovered_specialization_member_scope);
22891        assert!(
22892            declarations
22893                .iter()
22894                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
22895            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
22896        );
22897        assert!(
22898            declarations
22899                .iter()
22900                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
22901            "the recovered free function remains in its namespace: {declarations:#?}"
22902        );
22903        assert!(
22904            declarations
22905                .iter()
22906                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
22907            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
22908        );
22909        assert!(
22910            declarations.iter().all(|unit| {
22911                !unit
22912                    .short_name()
22913                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
22914            }),
22915            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
22916        );
22917        assert!(
22918            declarations
22919                .iter()
22920                .all(|unit| !unit.identifier().is_empty()),
22921            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
22922        );
22923    }
22924
22925    /// #4292: a recovered namespace level whose close the brace stack could not
22926    /// prove keeps its head-only range, and the first declaration the walk
22927    /// reaches inside the region is not the namespace. The Module's name
22928    /// occurrence must be the level's own name token; deriving it from the
22929    /// walk's current node paired the head range with an unrelated name and
22930    /// panicked the collector's containment assertion.
22931    #[test]
22932    fn recovered_namespace_levels_name_their_own_name_tokens() {
22933        // #3084's collapsed-head fixture without the namespace's own close:
22934        // the file ends inside the namespace, so the brace stack proves no
22935        // close for its `{` and the level keeps its `namespace Name {` head
22936        // alone (#3328).
22937        let source = r#"namespace outer::app {
22938
22939    class Target {
22940        int value_;
22941    };
22942
22943    template <typename T>
22944    class Holder {
22945    public:
22946        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
22947
22948#define HOLDER_DEFINE_OP( id, op )                                             \
22949    template <typename U>                                                      \
22950    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
22951        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
22952        return Target{};                                                       \
22953    }
22954
22955        HOLDER_DEFINE_OP( equal, == )
22956#undef HOLDER_DEFINE_OP
22957        T m_lhs;
22958    };
22959
22960    class Tail {};
22961"#;
22962        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
22963        let mut parser = tree_sitter::Parser::new();
22964        parser
22965            .set_language(&tree_sitter_cpp::LANGUAGE.into())
22966            .unwrap();
22967        let tree = parser.parse(source, None).unwrap();
22968        assert!(
22969            tree.root_node().has_error(),
22970            "the fixture must reproduce the parse damage"
22971        );
22972        let target_start = source.find("class Target").expect("target class");
22973        let target_node = tree
22974            .root_node()
22975            .named_descendant_for_byte_range(target_start, target_start + "class".len())
22976            .expect("target class AST node");
22977        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
22978        // The macro-declaration grammar keeps the namespace head and its
22979        // members inside a surviving `namespace_definition`; the missing close
22980        // no longer collapses the head into an ERROR, so the scope authority
22981        // answers from parsed ancestry rather than a recovered region.
22982        assert_eq!(
22983            index.enclosing_namespace_components(target_node, source),
22984            ["outer", "app"]
22985        );
22986        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
22987        let mut recovered_parsed = ParsedFile::new(String::new());
22988        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
22989        let properties = RefCell::new(HashMap::default());
22990        let dependencies =
22991            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
22992        let ancestry = ParentIndex::new(tree.root_node());
22993        let mut visitor = CppVisitor {
22994            file: &file,
22995            source,
22996            parsed: &mut recovered_parsed,
22997            source_collector: &collector,
22998            source_properties: &properties,
22999            source_dependencies: &dependencies,
23000            primary_ancestry: &ancestry,
23001            primary_root_id: tree.root_node().id(),
23002            source_links: HashMap::default(),
23003            source_declarations: HashSet::default(),
23004            preprocessor_context_cache: Default::default(),
23005            active_source_context: None,
23006            c_tag_semantics: false,
23007            c_tag_scope_witness: false,
23008            recovered_class_sibling_scopes: HashMap::default(),
23009            consumed_fragment_regions: Vec::new(),
23010            orphaned_namespaces: &index,
23011            partitioned_regions: Vec::new(),
23012            namespace_forward_scans: HashMap::default(),
23013            recovery_captures: Vec::new(),
23014            object_macro_fields: HashMap::default(),
23015            ambiguous_object_macro_fields: HashSet::default(),
23016        };
23017        // The surviving `namespace_definition` takes the ordinary namespace
23018        // visit, which declares each level with its own name token.
23019        visitor.visit_container(
23020            tree.root_node(),
23021            &ancestry,
23022            "",
23023            None,
23024            None,
23025            None,
23026            Vec::new(),
23027            Arc::default(),
23028        );
23029        let collector = collector.borrow();
23030        for (module_name, expected_name) in [
23031            (
23032                "outer",
23033                Range {
23034                    start_byte: 10,
23035                    end_byte: 15,
23036                    start_line: 1,
23037                    end_line: 1,
23038                },
23039            ),
23040            (
23041                "outer::app",
23042                Range {
23043                    start_byte: 17,
23044                    end_byte: 20,
23045                    start_line: 1,
23046                    end_line: 1,
23047                },
23048            ),
23049        ] {
23050            let declaration = visitor
23051                .source_links
23052                .iter()
23053                .find(|(unit, _)| unit.fq_name() == module_name)
23054                .expect("the recovered module links a source declaration")
23055                .1
23056                .last()
23057                .expect("the recovered module declaration");
23058            let declaration = collector.declaration(*declaration);
23059            let name = declaration
23060                .name
23061                .expect("the recovered module records its name occurrence");
23062            assert_eq!(
23063                collector.occurrence(name).range,
23064                expected_name,
23065                "the recovered module names its own token: {module_name}"
23066            );
23067        }
23068        assert!(
23069            parsed.declarations().iter().any(|unit| {
23070                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23071            }),
23072            "the recovered namespace members keep their namespace: {:#?}",
23073            parsed.declarations()
23074        );
23075    }
23076
23077    #[test]
23078    #[hardwon(
23079        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23080    )]
23081    fn object_like_field_macros_materialize_owner_specific_declarations() {
23082        let source = r#"#define PUBLIC_FIELDS int public_value;
23083#define PRIVATE_FIELDS int private_value;
23084#define NOT_A_FIELD_LIST not a declaration
23085
23086struct First {
23087  PUBLIC_FIELDS
23088  PRIVATE_FIELDS
23089};
23090struct Second {
23091  PUBLIC_FIELDS
23092  NOT_A_FIELD_LIST
23093};
23094#undef PUBLIC_FIELDS
23095struct Third {
23096  PUBLIC_FIELDS
23097};
23098"#;
23099        let parsed = parse_cpp_declarations(source, "macro-fields.c");
23100        let fields = parsed
23101            .declarations()
23102            .iter()
23103            .filter(|unit| unit.is_field())
23104            .map(|unit| unit.fq_name())
23105            .collect::<Vec<_>>();
23106
23107        assert!(
23108            fields.contains(&"First.public_value".to_string()),
23109            "{fields:?}"
23110        );
23111        assert!(
23112            fields.contains(&"First.private_value".to_string()),
23113            "{fields:?}"
23114        );
23115        assert!(
23116            fields.contains(&"Second.public_value".to_string()),
23117            "{fields:?}"
23118        );
23119        assert!(
23120            !fields.iter().any(|field| field.contains("not_a_field")),
23121            "malformed macro must fail closed: {fields:?}"
23122        );
23123        assert!(
23124            !fields.iter().any(|field| field.starts_with("Third.")),
23125            "undefined macro must fail closed: {fields:?}"
23126        );
23127    }
23128
23129    #[test]
23130    #[hardwon(
23131        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23132    )]
23133    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23134        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23135        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23136        let mut macros = parsed
23137            .declarations()
23138            .iter()
23139            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23140            .collect::<Vec<_>>();
23141        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23142
23143        assert_eq!(macros.len(), 2, "{macros:#?}");
23144        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23145        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23146        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23147        assert_eq!(
23148            parsed.declaration_ranges(macros[1])[0].start_byte,
23149            source.rfind("#define VALUE 2").expect("second definition")
23150        );
23151    }
23152
23153    #[test]
23154    #[hardwon(
23155        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23156    )]
23157    fn identifies_export_macro_class_base_displaced_into_declarator() {
23158        let source = r#"#define PROJECT_API_
23159namespace project {
23160namespace internal {
23161template <typename T>
23162class Base {};
23163}
23164template <typename T>
23165class Wrapper;
23166template <>
23167class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23168}
23169"#;
23170        let mut parser = tree_sitter::Parser::new();
23171        parser
23172            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23173            .unwrap();
23174        let tree = parser.parse(source, None).unwrap();
23175        let start = source.find("internal::Base<int>").expect("base");
23176        let mut base = tree
23177            .root_node()
23178            .descendant_for_byte_range(start, start + 8)
23179            .expect("base syntax");
23180        while base.kind() != "qualified_identifier" {
23181            base = base.parent().expect("qualified base ancestor");
23182        }
23183        assert!(
23184            is_recovered_exported_class_base_type_node(base, source),
23185            "{}",
23186            tree.root_node().to_sexp()
23187        );
23188    }
23189
23190    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23191        parsed
23192            .declarations()
23193            .iter()
23194            .filter(|unit| unit.is_function())
23195            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23196            .collect()
23197    }
23198
23199    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23200        let mut identities = all_function_identities(parsed)
23201            .into_iter()
23202            .map(|(name, signature)| (name, signature.unwrap_or_default()))
23203            .collect::<Vec<_>>();
23204        identities.sort();
23205        identities
23206    }
23207
23208    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
23209    /// prototype macros. Each case is parsed independently so a preceding
23210    /// malformed line cannot flatten the next declaration's fields into one
23211    /// cascading `ERROR`; that shape has no structural proof and must fail
23212    /// closed rather than fall back to a token scan. Parameter names are
23213    /// dropped from signatures just as they are for ordinary C prototypes.
23214    #[test]
23215    #[hardwon(
23216        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23217    )]
23218    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23219        let cases = [
23220            (
23221                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23222                "pg_typemap_fit_to_result",
23223                "(VALUE, VALUE)",
23224            ),
23225            (
23226                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23227                "pg_typemap_result_value",
23228                "(t_typemap *, VALUE, int, int)",
23229            ),
23230            (
23231                "void pg_typemap_mark _(( void * ));",
23232                "pg_typemap_mark",
23233                "(void *)",
23234            ),
23235            (
23236                "void init_pg_type_map _(( void ));",
23237                "init_pg_type_map",
23238                "(void)",
23239            ),
23240            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23241            (
23242                "extern VALUE pg_extern _(( VALUE, VALUE ));",
23243                "pg_extern",
23244                "(VALUE, VALUE)",
23245            ),
23246            (
23247                "size_t pg_typemap_memsize _(( const void * ));",
23248                "pg_typemap_memsize",
23249                "(const void *)",
23250            ),
23251            (
23252                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23253                "pg_wrap_socket_io",
23254                "(int, VALUE, VALUE *, int *)",
23255            ),
23256            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23257            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23258            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23259        ];
23260
23261        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23262            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23263            assert_eq!(
23264                function_identities(&parsed),
23265                vec![(name.to_string(), signature.to_string())],
23266                "{source}: {:#?}",
23267                parsed.declarations()
23268            );
23269            assert!(
23270                parsed.declarations().iter().all(|unit| !unit.is_field()),
23271                "{source} must not retain the malformed field: {:#?}",
23272                parsed.declarations()
23273            );
23274        }
23275    }
23276
23277    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
23278    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
23279    /// it as a multiplication of a type by a call
23280    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
23281    /// wrapped in an `expression_statement` that `has_error()` only because
23282    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
23283    /// here with a clean preceding field so the prototype remains the
23284    /// parser-owned `expression_statement` this recovery requires.
23285    #[test]
23286    #[hardwon(
23287        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23288    )]
23289    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23290        let cases = [
23291            (
23292                "PGconn *pg_get_pgconn _(( VALUE ));",
23293                "pg_get_pgconn",
23294                "(VALUE)",
23295            ),
23296            (
23297                "PGresult* pgresult_get _(( VALUE ));",
23298                "pgresult_get",
23299                "(VALUE)",
23300            ),
23301        ];
23302        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23303            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23304            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23305            assert_eq!(
23306                function_identities(&parsed),
23307                vec![(name.to_string(), signature.to_string())],
23308                "{source}: {:#?}",
23309                parsed.declarations()
23310            );
23311            assert_eq!(
23312                parsed
23313                    .declarations()
23314                    .iter()
23315                    .filter(|unit| unit.is_field())
23316                    .map(|unit| unit.fq_name())
23317                    .collect::<Vec<_>>(),
23318                vec!["rb_mPG".to_string()],
23319                "{source}: {:#?}",
23320                parsed.declarations()
23321            );
23322        }
23323    }
23324
23325    /// #6902. A class-body prototype macro line is a `field_declaration`. It
23326    /// takes the same candidate gate and reparse as a file-scope one, inside
23327    /// the class envelope, so the member keeps its owner, its written range,
23328    /// its `static`, and the access its label states.
23329    #[test]
23330    #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23331    fn c_prototype_macro_recovers_class_body_members() {
23332        const SOURCE: &str = concat!(
23333            "class Pg {\n",
23334            "  VALUE wrap _(( VALUE self ));\n",
23335            "public:\n",
23336            "  int invoke __P((int value));\n",
23337            "  static char *name PROTO(( int index ));\n",
23338            "};\n",
23339        );
23340        let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23341        assert_eq!(
23342            function_identities(&parsed),
23343            vec![
23344                ("Pg.invoke".to_string(), "(int)".to_string()),
23345                ("Pg.name".to_string(), "(int)".to_string()),
23346                ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23347            ],
23348            "{:#?}",
23349            parsed.declarations()
23350        );
23351        assert!(
23352            parsed.declarations().iter().all(|unit| !unit.is_field()),
23353            "the malformed fields must not survive: {:#?}",
23354            parsed.declarations()
23355        );
23356        let class = parsed
23357            .declarations()
23358            .iter()
23359            .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23360            .expect("class Pg");
23361        let mut children = parsed
23362            .children
23363            .get(class)
23364            .into_iter()
23365            .flatten()
23366            .map(|unit| unit.fq_name())
23367            .collect::<Vec<_>>();
23368        children.sort();
23369        assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23370        for (name, written) in [
23371            ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23372            ("Pg.invoke", "int invoke __P((int value));"),
23373            ("Pg.name", "static char *name PROTO(( int index ));"),
23374        ] {
23375            let unit = parsed
23376                .declarations()
23377                .iter()
23378                .find(|unit| unit.is_function() && unit.fq_name() == name)
23379                .unwrap_or_else(|| panic!("missing recovered member {name}"));
23380            let [range] = parsed.declaration_ranges(unit) else {
23381                panic!("{name} must have exactly one range");
23382            };
23383            assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23384        }
23385        assert_eq!(
23386            recorded_callable_modifiers(&parsed),
23387            vec![
23388                "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23389                    .to_string(),
23390                "Pg.name decl_only=true static=true constructor=false visibility=Public"
23391                    .to_string(),
23392                "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23393                    .to_string(),
23394            ],
23395        );
23396    }
23397
23398    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
23399    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
23400    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
23401    /// regions. The witnessed declaration must still become a Function and
23402    /// its type must not survive as the name of a bogus Field. This test does
23403    /// not claim that structurally flattened neighboring prototypes are safe
23404    /// to recover.
23405    #[test]
23406    #[hardwon(
23407        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23408    )]
23409    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23410        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
23411VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
23412int pg_typemap_fit_to_copy_get                         _(( VALUE ));
23413VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
23414t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
23415VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
23416void pg_typemap_mark                                   _(( void * ));
23417size_t pg_typemap_memsize                              _(( const void * ));
23418void pg_typemap_compact                                _(( void * ));
23419
23420PGconn *pg_get_pgconn                                  _(( VALUE ));
23421t_pg_connection *pg_get_connection                     _(( VALUE ));
23422VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
23423#ifdef __GNUC__
23424__attribute__((format(printf, 3, 4)))
23425#endif
23426NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
23427VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23428void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23429
23430
23431VALUE pg_new_result                                    _(( PGresult *, VALUE ));
23432VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
23433PGresult* pgresult_get                                 _(( VALUE ));
23434VALUE pg_result_check                                  _(( VALUE ));
23435VALUE pg_result_clear                                  _(( VALUE ));
23436VALUE pg_tuple_new                                     _(( VALUE, int ));
23437
23438/*
23439 * Fetch the data pointer for the result object
23440 */
23441static inline t_pg_result *
23442pgresult_get_this( VALUE self )
23443{
23444	return RTYPEDDATA_DATA(self);
23445}
23446
23447
23448rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
23449const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
23450rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
23451
23452"#;
23453        let parsed = parse_cpp_declarations(source, "pg.h");
23454        assert!(
23455            function_identities(&parsed)
23456                .iter()
23457                .any(|(name, signature)| name == "pg_typemap_result_value"
23458                    && signature == "(t_typemap *, VALUE, int, int)"),
23459            "the real issue witness must be a Function with its C signature: {:#?}",
23460            parsed.declarations()
23461        );
23462        assert!(
23463            parsed
23464                .declarations()
23465                .iter()
23466                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23467            "the issue witness must not leave its return type as a Field name: {:#?}",
23468            parsed.declarations()
23469        );
23470    }
23471
23472    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
23473    /// between `explicit` and a constructor name onto the name, producing a
23474    /// `qualified_identifier` whose `::` it had to invent. The declared member
23475    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
23476    /// macro in type position, was already recovered and is the control that
23477    /// both spellings agree.
23478    #[test]
23479    #[hardwon(
23480        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23481    )]
23482    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23483        let source = r#"class SIMD_4x26 final {
23484   public:
23485      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23486      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23487      int m_v;
23488};
23489"#;
23490        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23491        assert_eq!(
23492            function_identities(&parsed),
23493            vec![
23494                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
23495                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
23496            ],
23497            "{:#?}",
23498            parsed.declarations()
23499        );
23500    }
23501
23502    #[test]
23503    #[hardwon(
23504        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
23505    )]
23506    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
23507        let source = r#"struct cpu_spec {};
23508static struct cpu_spec __initdata cpu_specs[];
23509static struct cpu_spec ns::cpu_specs[];
23510"#;
23511        let tree = crate::diagnostics::parse_cpp_tree(source);
23512        let root = tree.root_node();
23513        let declarations = root
23514            .named_children(&mut root.walk())
23515            .filter(|node| node.kind() == "declaration")
23516            .collect::<Vec<_>>();
23517        let names = declarations
23518            .iter()
23519            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
23520            .filter_map(|declarator| extract_variable_name(declarator, source))
23521            .collect::<Vec<_>>();
23522        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
23523    }
23524
23525    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
23526    /// `ERROR` holding the wrapped declaration and every declaration after it
23527    /// until the parser recovers. All of them must be indexed, each with the
23528    /// byte range that spells it.
23529    #[test]
23530    #[hardwon(
23531        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
23532    )]
23533    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
23534        let source = r#"#include <cstdint>
23535struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
23536    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
23537                     struct llama_model * model,
23538              struct llama_context_params   params),
23539            "use llama_init_from_model instead");
23540    LLAMA_API int32_t llama_tokenize(
23541        const struct llama_vocab * vocab,
23542                      const char * text,
23543                            bool   parse_special);
23544    LLAMA_API int32_t llama_other(int a);
23545"#;
23546        let parsed = parse_cpp_declarations(source, "llama.h");
23547        assert_eq!(
23548            function_identities(&parsed),
23549            vec![
23550                (
23551                    "llama_new_context_with_model".to_string(),
23552                    "(struct llama_model *, struct llama_context_params)".to_string()
23553                ),
23554                ("llama_other".to_string(), "(int)".to_string()),
23555                (
23556                    "llama_tokenize".to_string(),
23557                    "(const struct llama_vocab *, const char *, bool)".to_string()
23558                ),
23559            ],
23560            "{:#?}",
23561            parsed.declarations()
23562        );
23563
23564        // Each recovered declaration owns the source that spells it, so
23565        // navigation lands on the declaration and not on the macro envelope.
23566        for (name, expected) in [
23567            (
23568                "llama_new_context_with_model",
23569                "LLAMA_API struct llama_context * llama_new_context_with_model(",
23570            ),
23571            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
23572            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
23573        ] {
23574            let unit = parsed
23575                .declarations()
23576                .iter()
23577                .find(|unit| unit.is_function() && unit.fq_name() == name)
23578                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
23579            let [range] = parsed.declaration_ranges(unit) else {
23580                panic!("{name} must have exactly one range");
23581            };
23582            let text = &source[range.start_byte..range.end_byte];
23583            assert!(
23584                text.starts_with(expected),
23585                "{name} range is {text:?}, expected it to start with {expected:?}"
23586            );
23587            assert!(
23588                text.ends_with(')') || text.ends_with(';'),
23589                "{name}: {text:?}"
23590            );
23591        }
23592    }
23593
23594    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
23595    /// invocation's `(` at the end of its line. The macro-declaration grammar
23596    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
23597    /// declaration and the hint's exploded words sit in the parameter list,
23598    /// the invocation's `)` closes that list, and its `;` is left to the
23599    /// sibling `expression_statement`. (Before the macro-declaration grammar,
23600    /// the second invocation's envelope swallowed the third into a run of bare
23601    /// siblings; #3094 read that flattening from the token order, and the
23602    /// collapsed-run recovery still owns the envelope/statement pairing the
23603    /// same way.)
23604    ///
23605    /// The identifiers are `library_*` at whisper's own lengths on purpose:
23606    /// tree-sitter prices error recovery partly by how much input a repair
23607    /// skips, and the same construct written with shorter names parses cleanly.
23608    #[test]
23609    #[hardwon(
23610        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
23611    )]
23612    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
23613        let source = r#"    LIBRARY_DEPRECATED(
23614        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23615        "use library_init_from_file_with_params instead"
23616    );
23617    LIBRARY_DEPRECATED(
23618        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
23619        "use library_init_from_buffer_with_params instead"
23620    );
23621    LIBRARY_DEPRECATED(
23622        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
23623        "use library_init_with_params instead"
23624    );
23625"#;
23626        let mut parser = tree_sitter::Parser::new();
23627        parser
23628            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23629            .expect("C++ grammar");
23630        let tree = parser.parse(source, None).expect("C++ tree");
23631        let root = tree.root_node();
23632
23633        // The shape this recovery exists for: three sibling `ERROR` envelopes,
23634        // each holding one whole invocation, each followed by the statement
23635        // that owns the invocation's `;`.
23636        let mut cursor = root.walk();
23637        let items = root.children(&mut cursor).collect::<Vec<_>>();
23638        let [
23639            first,
23640            first_statement,
23641            second,
23642            second_statement,
23643            third,
23644            third_statement,
23645        ] = items.as_slice()
23646        else {
23647            panic!("{}", root.to_sexp());
23648        };
23649        let envelopes = [
23650            (first, first_statement),
23651            (second, second_statement),
23652            (third, third_statement),
23653        ];
23654        for (envelope, statement) in envelopes {
23655            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
23656            assert_eq!(
23657                statement.kind(),
23658                "expression_statement",
23659                "{}",
23660                root.to_sexp()
23661            );
23662            assert!(
23663                node_text(*envelope, source).ends_with(')'),
23664                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
23665                root.to_sexp()
23666            );
23667        }
23668
23669        // Each is admitted, and each names the byte just past its own `;`. A
23670        // self-contained invocation swallows nothing, so the recovery owns
23671        // only its own bytes.
23672        let ancestry = ParentIndex::new(root);
23673        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
23674            .expect("the first invocation");
23675        assert_eq!(
23676            &source[..first_run.invocation_end],
23677            &source[..source.find("instead\"\n    );").expect("first hint")
23678                + "instead\"\n    );".len()]
23679        );
23680        for (envelope, _) in envelopes {
23681            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
23682                .expect("each invocation");
23683            assert_eq!(
23684                run.region_end, run.invocation_end,
23685                "a self-contained invocation owns only its own bytes"
23686            );
23687        }
23688
23689        let parsed = parse_cpp_declarations(source, "library.h");
23690        assert_eq!(
23691            function_identities(&parsed),
23692            vec![
23693                (
23694                    "library_init".to_string(),
23695                    "(struct library_model_loader *)".to_string()
23696                ),
23697                (
23698                    "library_init_from_buffer".to_string(),
23699                    "(void *, size_t)".to_string()
23700                ),
23701                (
23702                    "library_init_from_file".to_string(),
23703                    "(const char *)".to_string()
23704                ),
23705            ],
23706            "{:#?}",
23707            parsed.declarations()
23708        );
23709    }
23710
23711    #[test]
23712    #[hardwon(
23713        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
23714    )]
23715    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
23716        let source = r#"void host() {
23717#ifdef ENABLED
23718LIBRARY_DEPRECATED(
23719    int hidden(const char* value),
23720    "hint"
23721);
23722#endif
23723}
23724"#;
23725        let mut parser = tree_sitter::Parser::new();
23726        parser
23727            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23728            .expect("C++ grammar");
23729        let tree = parser.parse(source, None).expect("C++ tree");
23730        let root = tree.root_node();
23731        let function = root.named_child(0).expect("ordinary function");
23732        let body = function
23733            .child_by_field_name("body")
23734            .expect("ordinary function body");
23735        let conditional = body.named_child(0).expect("conditional body");
23736        let mut conditional_cursor = conditional.walk();
23737        let envelope = conditional
23738            .named_children(&mut conditional_cursor)
23739            .find(|child| child.kind() == "ERROR")
23740            .expect("macro error");
23741        assert_eq!(conditional.kind(), "preproc_ifdef");
23742        assert_eq!(envelope.kind(), "ERROR");
23743
23744        let ancestry = ParentIndex::new(root);
23745        assert!(declaration_scope_container(envelope, &ancestry).is_none());
23746        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
23747
23748        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
23749        assert!(
23750            parsed
23751                .declarations()
23752                .iter()
23753                .all(|unit| unit.fq_name() != "hidden"),
23754            "a function-body macro error must not become a global declaration: {:#?}",
23755            parsed.declarations()
23756        );
23757    }
23758
23759    /// The hand-off that keeps the scan finite. An invocation that reaches the
23760    /// end of its own declaration scope swallowed nothing, and the ordinary
23761    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
23762    /// that region for every invocation it finds, so admitting it here would
23763    /// hand the scan its own input back.
23764    #[test]
23765    #[hardwon(
23766        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
23767    )]
23768    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
23769        let source = r#"LIBRARY_DEPRECATED(
23770        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
23771        "use library_init_from_file_with_params instead"
23772    );"#;
23773        let mut parser = tree_sitter::Parser::new();
23774        parser
23775            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23776            .expect("C++ grammar");
23777        let tree = parser.parse(source, None).expect("C++ tree");
23778        let root = tree.root_node();
23779        let head = root.named_child(0).expect("the invocation");
23780        let ancestry = ParentIndex::new(root);
23781        assert!(
23782            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
23783            "{}",
23784            root.to_sexp()
23785        );
23786        assert!(
23787            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
23788            "the ordinary reader must be the one that has it: {}",
23789            root.to_sexp()
23790        );
23791        // The index is a speed substitution, never a semantic one: the
23792        // unindexed path is `Node::parent` itself and must agree.
23793        let unindexed = ParentIndex::unindexed();
23794        assert!(
23795            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
23796            "indexed and unindexed climbs must agree"
23797        );
23798        assert_eq!(
23799            macro_wrapped_declarations(head, source, &ancestry).len(),
23800            macro_wrapped_declarations(head, source, &unindexed).len(),
23801            "indexed and unindexed climbs must agree"
23802        );
23803    }
23804
23805    /// Negative controls for the same recovery: a macro invocation whose
23806    /// arguments are not a declaration recovers nothing, whether the parser
23807    /// keeps it clean, reads the arguments as a type, or reads them as a bare
23808    /// declarator.
23809    #[test]
23810    #[hardwon(
23811        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
23812    )]
23813    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
23814        for source in [
23815            "int before;\nFOO(1, 2);\nint after;\n",
23816            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
23817            "int before;\nMACRO(int a, int b);\nint after;\n",
23818            "DECLARE_HANDLE(HWND);\nint after;\n",
23819        ] {
23820            let parsed = parse_cpp_declarations(source, "macro-call.h");
23821            assert_eq!(
23822                function_identities(&parsed),
23823                Vec::new(),
23824                "{source:?} must declare no function: {:#?}",
23825                parsed.declarations()
23826            );
23827        }
23828    }
23829
23830    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
23831    /// makes tree-sitter read the macro as the member's type and its argument
23832    /// list as a parenthesized declarator, which then swallows the attributed
23833    /// member and the member written after it. Both are members.
23834    #[test]
23835    #[hardwon(
23836        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
23837    )]
23838    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
23839        let source = r#"#include <string_view>
23840namespace Botan {
23841class DL_Group final {
23842   public:
23843      DL_Group() = default;
23844      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23845      DL_Group(std::string_view pem, int format);
23846      size_t get_p() const;
23847};
23848}
23849"#;
23850        let parsed = parse_cpp_declarations(source, "dl_group.h");
23851        assert_eq!(
23852            function_identities(&parsed),
23853            vec![
23854                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
23855                (
23856                    "Botan.DL_Group.DL_Group".to_string(),
23857                    "(std::string_view)".to_string()
23858                ),
23859                (
23860                    "Botan.DL_Group.DL_Group".to_string(),
23861                    "(std::string_view, int)".to_string()
23862                ),
23863                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23864            ],
23865            "{:#?}",
23866            parsed.declarations()
23867        );
23868    }
23869
23870    /// #2552 shape 3, export-macro class. The class head macro makes the body a
23871    /// `compound_statement`, so members come from the region reparse, and one
23872    /// string-attribute member used to make that reparse unindexable -- which
23873    /// left the class with no members at all.
23874    #[test]
23875    #[hardwon(
23876        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
23877    )]
23878    fn an_export_macro_class_keeps_its_string_attribute_members() {
23879        let source = r#"#include <string_view>
23880namespace Botan {
23881class BOTAN_PUBLIC_API(2, 0) DL_Group final {
23882   public:
23883      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
23884      DL_Group(std::string_view pem, int format);
23885      size_t get_p() const;
23886};
23887}
23888"#;
23889        let parsed = parse_cpp_declarations(source, "dl_group.h");
23890        assert!(
23891            parsed
23892                .declarations()
23893                .iter()
23894                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
23895            "{:#?}",
23896            parsed.declarations()
23897        );
23898        assert_eq!(
23899            function_identities(&parsed),
23900            vec![
23901                (
23902                    "Botan.DL_Group.DL_Group".to_string(),
23903                    "(std::string_view)".to_string()
23904                ),
23905                (
23906                    "Botan.DL_Group.DL_Group".to_string(),
23907                    "(std::string_view, int)".to_string()
23908                ),
23909                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
23910            ],
23911            "{:#?}",
23912            parsed.declarations()
23913        );
23914    }
23915
23916    /// #2552 shape 3, the `= default` variant plus the access-label
23917    /// constructor. The attributed defaulted constructor separates cleanly, but
23918    /// it strands the next member in a bare `ERROR`, and a constructor written
23919    /// with a member-initializer list under `private:` dissolves into
23920    /// expression soup that the reparse recovers from its own source range.
23921    #[test]
23922    #[hardwon(
23923        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
23924    )]
23925    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
23926        let source = r#"namespace Botan {
23927class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
23928   public:
23929      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
23930      XMSS_Parameters(int oid, int len);
23931      size_t len() const;
23932
23933   private:
23934      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
23935            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
23936
23937      int m_oid;
23938      int m_wots_oid;
23939      size_t m_element_size;
23940      size_t m_tree_height;
23941};
23942}
23943"#;
23944        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
23945        let constructors = function_identities(&parsed)
23946            .into_iter()
23947            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
23948            .map(|(_, signature)| signature)
23949            .collect::<Vec<_>>();
23950        assert_eq!(
23951            constructors,
23952            vec![
23953                "()".to_string(),
23954                "(int, int)".to_string(),
23955                "(int, int, size_t, size_t)".to_string(),
23956            ],
23957            "{:#?}",
23958            parsed.declarations()
23959        );
23960    }
23961
23962    /// Negative control for the same rule: a real qualified name spells its
23963    /// `::` in the source, so the separator is present rather than MISSING and
23964    /// the out-of-line definition keeps its owner.
23965    #[test]
23966    #[hardwon(
23967        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
23968    )]
23969    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
23970        let source = r#"namespace shell {
23971struct Outer {
23972   struct Inner {
23973      Inner(int v);
23974      void run(int v);
23975   };
23976};
23977Outer::Inner::Inner(int v) {}
23978void Outer::Inner::run(int v) {}
23979}
23980"#;
23981        let parsed = parse_cpp_declarations(source, "outer.cpp");
23982        let names = function_identities(&parsed)
23983            .into_iter()
23984            .map(|(fq_name, _)| fq_name)
23985            .collect::<Vec<_>>();
23986        assert!(
23987            names
23988                .iter()
23989                .all(|name| name.starts_with("shell.Outer$Inner.")),
23990            "{names:#?}"
23991        );
23992    }
23993
23994    #[test]
23995    #[hardwon(
23996        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
23997    )]
23998    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
23999        let source = r#"namespace control {
24000template <typename T>
24001class AnySpan;
24002template <typename T>
24003class ABSL_ATTRIBUTE_VIEW AnySpan {
24004 public:
24005  int begin() const;
24006};
24007}
24008
24009namespace absl {
24010ABSL_NAMESPACE_BEGIN
24011template <typename T>
24012class ABSL_ATTRIBUTE_VIEW Span {
24013 public:
24014  int begin() const;
24015  int back() const;
24016};
24017
24018int begin();
24019int back();
24020}
24021"#;
24022        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24023        let declarations = parsed.declarations();
24024        assert!(
24025            declarations
24026                .iter()
24027                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24028        );
24029        for method in ["begin", "back"] {
24030            assert!(declarations.iter().any(|unit| {
24031                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24032            }));
24033            assert!(
24034                declarations.iter().any(|unit| {
24035                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
24036                })
24037            );
24038        }
24039        assert!(
24040            declarations
24041                .iter()
24042                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24043        );
24044        assert!(
24045            declarations
24046                .iter()
24047                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24048        );
24049        assert!(
24050            declarations
24051                .iter()
24052                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24053        );
24054    }
24055
24056    #[test]
24057    #[hardwon(
24058        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24059    )]
24060    fn explicit_global_member_definition_has_canonical_package_boundary() {
24061        let source = r#"
24062namespace arangodb::aql {
24063class ExecutionPlan {
24064 public:
24065  template<class... Args> Node* createNode(Args&&... args);
24066};
24067}
24068
24069template<class... Args>
24070Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24071"#;
24072        let parsed = parse_cpp_declarations(source, "global-member.cpp");
24073
24074        assert!(parsed.declarations().iter().any(|unit| {
24075            unit.is_function()
24076                && unit.package_name() == "arangodb::aql"
24077                && unit.short_name() == "ExecutionPlan.createNode"
24078                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24079        }));
24080    }
24081
24082    #[test]
24083    #[hardwon(
24084        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24085    )]
24086    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24087        let source = r#"
24088#ifndef TINYXML2_INCLUDED
24089#define TINYXML2_INCLUDED
24090namespace tinyxml2 {
24091class TINYXML2_LIB XMLUtil {
24092 public:
24093  static const char* SkipWhiteSpace(const char* p) {
24094    while (*p) {
24095      if (*p == ' ') {
24096        ++p;
24097      }
24098    }
24099    return p;
24100  }
24101  static bool StringEqual(const char* p, const char* q) {
24102    return p == q;
24103  }
24104  class TINYXML2_LIB Helper {
24105   public:
24106    void Touch();
24107  };
24108  static void ToStr(int value, char* buffer);
24109 private:
24110  static const char* writeBoolTrue;
24111};
24112
24113class TINYXML2_LIB XMLNode {
24114 public:
24115  virtual XMLNode* ShallowClone() const = 0;
24116  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24117};
24118}
24119#endif
24120"#;
24121        let mut parser = tree_sitter::Parser::new();
24122        parser
24123            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24124            .unwrap();
24125        let tree = parser.parse(source, None).unwrap();
24126        let mut boundary_found = false;
24127        walk_named_tree_preorder(tree.root_node(), true, |node| {
24128            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24129                && name == "XMLUtil"
24130            {
24131                boundary_found = fragmented_export_sibling_class_boundary(
24132                    node,
24133                    source,
24134                    &ParentIndex::unindexed(),
24135                )
24136                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24137                .is_some_and(|(_, name, _, _)| name == "XMLNode");
24138            }
24139            WalkControl::Continue
24140        });
24141        assert!(
24142            boundary_found,
24143            "fixture must exercise the recovered sibling boundary"
24144        );
24145
24146        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24147        assert!(
24148            parsed
24149                .declarations()
24150                .iter()
24151                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24152            "{:#?}",
24153            parsed.declarations()
24154        );
24155        assert!(
24156            parsed
24157                .declarations()
24158                .iter()
24159                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24160            "{:#?}",
24161            parsed.declarations()
24162        );
24163        assert!(parsed.declarations().iter().any(|unit| {
24164            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24165        }));
24166        assert!(
24167            parsed
24168                .declarations()
24169                .iter()
24170                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24171        );
24172        assert!(
24173            parsed
24174                .declarations()
24175                .iter()
24176                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24177        );
24178    }
24179
24180    #[test]
24181    #[hardwon(
24182        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24183    )]
24184    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24185        // Clang's diagnostic suite intentionally contains this ill-formed
24186        // spelling. The analyzer must retain the parser's explicit-global AST
24187        // boundary instead of constructing `cwg311::::cwg311::X`.
24188        let parsed = parse_cpp_declarations(
24189            r#"
24190namespace cwg311 {
24191namespace X { namespace Y {} }
24192namespace ::cwg311::X {}
24193}
24194"#,
24195            "explicit-global-namespace.cpp",
24196        );
24197
24198        assert!(parsed.declarations().iter().any(|unit| {
24199            unit.kind() == CodeUnitType::Module
24200                && unit.short_name() == "cwg311::X"
24201                && unit.fq_name() == "cwg311::X"
24202        }));
24203        assert!(
24204            parsed
24205                .declarations()
24206                .iter()
24207                .all(|unit| !unit.short_name().contains("::::")),
24208            "recovered namespace names must not retain empty scope components: {:#?}",
24209            parsed.declarations()
24210        );
24211    }
24212
24213    #[test]
24214    #[hardwon(
24215        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24216    )]
24217    fn repeated_scope_separator_does_not_create_empty_function_owner() {
24218        let scope = ScopeInfo {
24219            package_name: "X".to_string(),
24220            module: None,
24221            class_unit: None,
24222            template_signature: None,
24223            template_metadata: None,
24224            declarations_are_fields: false,
24225            recovered_specialization_member_scope: false,
24226            visible_using_namespaces: Vec::new(),
24227            local_block: None,
24228            visible_using_declarations: Arc::default(),
24229        };
24230
24231        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24232
24233        assert!(owner.is_none());
24234        assert_eq!(name, "doit");
24235        assert_eq!(package, "X");
24236    }
24237
24238    #[test]
24239    #[hardwon(
24240        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24241    )]
24242    fn trailing_decltype_expression_is_not_a_function_declarator() {
24243        let source = r#"
24244namespace boost { namespace detail {
24245#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24246    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24247    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24248#define BOOST_THREAD_PROVIDES_INVOKE
24249#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24250template <class Fp, class A0, class ...Args>
24251inline auto
24252invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24253       BOOST_THREAD_RV_REF(Args) ...args)
24254    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24255{
24256    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24257}
24258#endif
24259#endif
24260}}
24261"#;
24262        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24263
24264        assert!(
24265            parsed
24266                .declarations()
24267                .iter()
24268                .all(|unit| unit.short_name() != ".*f")
24269        );
24270    }
24271
24272    fn find_class_named<'tree>(
24273        root: Node<'tree>,
24274        source: &str,
24275        expected_name: &str,
24276    ) -> Option<Node<'tree>> {
24277        let mut stack = vec![root];
24278        while let Some(node) = stack.pop() {
24279            if node.kind() == "class_specifier"
24280                && node
24281                    .child_by_field_name("name")
24282                    .is_some_and(|name| node_text(name, source) == expected_name)
24283            {
24284                return Some(node);
24285            }
24286            let mut cursor = node.walk();
24287            stack.extend(node.named_children(&mut cursor));
24288        }
24289        None
24290    }
24291
24292    #[test]
24293    #[hardwon(
24294        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24295    )]
24296    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24297        let source = r#"EXPORT void definition(struct Value value) {}
24298EXPORT void prototype(struct Value value);
24299"#;
24300        let mut parser = tree_sitter::Parser::new();
24301        parser
24302            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24303            .unwrap();
24304        let tree = parser.parse(source, None).unwrap();
24305        let root = tree.root_node();
24306        let mut cursor = root.walk();
24307        let callables = root
24308            .named_children(&mut cursor)
24309            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24310            .collect::<Vec<_>>();
24311
24312        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24313        for callable in callables {
24314            // The macro-declaration grammar parses these cleanly, with the
24315            // export macro retained as a `macro_modifier`; the preserved
24316            // callable and its `struct` parameter are what the sentinel gate
24317            // rejects on, with or without an ERROR in the envelope.
24318            assert!(
24319                cpp_sentinel_macro_parts(callable, source).is_none(),
24320                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24321            );
24322        }
24323    }
24324
24325    #[test]
24326    #[hardwon(
24327        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24328    )]
24329    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24330        let source = r#"namespace absl {
24331ABSL_NAMESPACE_BEGIN
24332// Generate a floating-point variate conforming to a Beta distribution:
24333template <typename RealType = double>
24334class beta_distribution {
24335 public:
24336  using result_type = RealType;
24337
24338
24339  beta_distribution() : beta_distribution(1) {}
24340
24341  explicit beta_distribution(result_type alpha, result_type beta = 1)
24342      : param_(alpha, beta) {}
24343
24344  explicit beta_distribution(const param_type& p) : param_(p) {}
24345
24346  void reset() {}
24347
24348  // Generating functions
24349  template <typename URBG>
24350  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
24351    return (*this)(g, param_);
24352  }
24353
24354};
24355ABSL_NAMESPACE_END
24356}  // namespace absl
24357"#;
24358        let mut parser = tree_sitter::Parser::new();
24359        parser
24360            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24361            .unwrap();
24362        let tree = parser.parse(source, None).unwrap();
24363        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24364        let body = namespace
24365            .child_by_field_name("body")
24366            .expect("fixture namespace body");
24367        let sentinel = body.named_child(0).expect("sentinel envelope");
24368
24369        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
24370        // envelope that ends at the class name, with the class body as the
24371        // sibling it recovered with; the bogus callable the older grammar
24372        // assembled from the last member is no longer part of the envelope.
24373        assert_eq!(sentinel.kind(), "ERROR");
24374        assert!(
24375            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24376            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24377        );
24378
24379        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24380        for expected in [
24381            "absl.beta_distribution",
24382            "absl.beta_distribution.beta_distribution",
24383            "absl.beta_distribution.reset",
24384        ] {
24385            assert!(
24386                parsed
24387                    .declarations()
24388                    .iter()
24389                    .any(|unit| unit.fq_name() == expected),
24390                "missing recovered declaration {expected}: {:#?}",
24391                parsed.declarations()
24392            );
24393        }
24394    }
24395
24396    #[test]
24397    #[hardwon(
24398        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24399    )]
24400    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24401        let source = r#"namespace absl {
24402ABSL_NAMESPACE_BEGIN
24403// absl::discrete_distribution
24404//
24405// A discrete distribution produces random integers i, where 0 <= i < n
24406template <typename IntType = int>
24407class discrete_distribution {
24408 public:
24409  using result_type = IntType;
24410  class param_type {
24411   public:
24412    param_type() { init(); }
24413    template <typename InputIterator>
24414    explicit param_type(InputIterator begin, InputIterator end)
24415        : p_(begin, end) {
24416      init();
24417    }
24418  };
24419  discrete_distribution() : param_() {}
24420  explicit discrete_distribution(const param_type& p) : param_(p) {}
24421};
24422ABSL_NAMESPACE_END
24423}  // namespace absl
24424"#;
24425        let mut parser = tree_sitter::Parser::new();
24426        parser
24427            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24428            .unwrap();
24429        let tree = parser.parse(source, None).unwrap();
24430        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24431        let body = namespace
24432            .child_by_field_name("body")
24433            .expect("fixture namespace body");
24434        let sentinel = body.named_child(0).expect("sentinel envelope");
24435        let callable = sentinel
24436            .child_by_field_name("declarator")
24437            .and_then(extract_function_declarator)
24438            .and_then(cpp_function_declarator_name_node)
24439            .expect("preserved callable name");
24440
24441        assert_eq!(sentinel.kind(), "function_definition");
24442        assert_eq!(callable.kind(), "identifier");
24443        assert!(
24444            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24445            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24446        );
24447    }
24448
24449    #[test]
24450    #[hardwon(
24451        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24452    )]
24453    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24454        let source = r#"
24455#define CPPCHECKLIB
24456class Library {
24457    struct Container {
24458        CPPCHECKLIB static std::string toString(Yield yield);
24459        CPPCHECKLIB static std::string toString(Action action);
24460    };
24461};
24462"#;
24463        let mut parser = tree_sitter::Parser::new();
24464        parser
24465            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24466            .unwrap();
24467        let tree = parser.parse(source, None).unwrap();
24468        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24469        let parsed = parse_cpp_file(&file, source, &tree);
24470        assert!(
24471            parsed
24472                .declarations()
24473                .iter()
24474                .all(|unit| unit.fq_name() != "Library$Container.std"),
24475            "the qualified return-type namespace must not become a field: {:#?}",
24476            parsed.declarations()
24477        );
24478        for expected in ["(Yield)", "(Action)"] {
24479            assert!(
24480                parsed.declarations().iter().any(|unit| {
24481                    unit.is_function()
24482                        && unit.fq_name() == "Library$Container.toString"
24483                        && unit.signature() == Some(expected)
24484                }),
24485                "recovered toString overload {expected} is missing: {:#?}",
24486                parsed.declarations()
24487            );
24488        }
24489    }
24490
24491    #[test]
24492    #[hardwon(
24493        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24494    )]
24495    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
24496        let source = r#"
24497#define SIMPLECPP_LIB
24498namespace simplecpp {
24499using TokenString = std::string;
24500struct Location { int line{}; };
24501class SIMPLECPP_LIB Token {
24502  TokenString prefix;
24503  void prefix_method() {}
24504 public:
24505  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24506      whitespaceahead(wsahead), location(loc), string(s)
24507      // The comment must not hide the constructor body from recovery.
24508      {
24509      flags();
24510  }
24511  TokenString string;
24512  bool whitespaceahead;
24513  Location location;
24514  Token *previous{};
24515 private:
24516  void flags() {
24517      whitespaceahead = true;
24518  }
24519};
24520}
24521"#;
24522        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
24523
24524        let location_fields = parsed
24525            .declarations()
24526            .iter()
24527            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
24528            .collect::<Vec<_>>();
24529        assert_eq!(
24530            location_fields.len(),
24531            1,
24532            "location should have one class-owned declaration: {:#?}",
24533            parsed.declarations()
24534        );
24535        assert!(
24536            location_fields[0].is_field(),
24537            "location has wrong kind: {:#?}",
24538            parsed.declarations()
24539        );
24540        assert!(
24541            parsed.declarations().iter().all(|unit| {
24542                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
24543            })
24544        );
24545        assert!(
24546            parsed.declarations().iter().all(|unit| {
24547                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24548            })
24549        );
24550        assert!(
24551            parsed
24552                .declarations()
24553                .iter()
24554                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
24555        );
24556        assert!(
24557            parsed
24558                .declarations()
24559                .iter()
24560                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
24561            "the recovered class must retain its constructor: {:#?}",
24562            parsed.declarations()
24563        );
24564        assert!(
24565            parsed
24566                .declarations()
24567                .iter()
24568                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
24569        );
24570        assert!(parsed.declarations().iter().any(|unit| {
24571            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
24572        }));
24573        let constructor = parsed
24574            .declarations()
24575            .iter()
24576            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
24577            .expect("recovered constructor");
24578        let constructor_start = source.find("Token(const").expect("constructor start");
24579        let constructor_end = source
24580            .get(
24581                ..source
24582                    .find("  TokenString string;")
24583                    .expect("constructor end"),
24584            )
24585            .expect("constructor slice")
24586            .trim_end()
24587            .len();
24588        assert!(
24589            parsed
24590                .navigation_ranges
24591                .get(constructor)
24592                .is_some_and(|ranges| {
24593                    ranges.iter().any(|range| {
24594                        range.start_byte == constructor_start && range.end_byte == constructor_end
24595                    })
24596                }),
24597            "constructor navigation must span the full body: {:#?}",
24598            parsed.navigation_ranges
24599        );
24600        assert_eq!(
24601            parsed
24602                .signature_metadata
24603                .get(constructor)
24604                .and_then(|metadata| metadata.first())
24605                .and_then(SignatureMetadata::callable_linkage),
24606            Some(CallableLinkage::External)
24607        );
24608        let token_class = parsed
24609            .declarations()
24610            .iter()
24611            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
24612            .expect("recovered Token class");
24613        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
24614        assert!(
24615            parsed
24616                .navigation_ranges
24617                .get(token_class)
24618                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
24619            "class navigation must include the terminating semicolon: {:#?}",
24620            parsed.navigation_ranges
24621        );
24622    }
24623
24624    #[test]
24625    #[hardwon(
24626        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24627    )]
24628    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
24629        let source = r#"
24630#define SIMPLECPP_LIB
24631namespace simplecpp {
24632using TokenString = std::string;
24633class Macro;
24634struct Location {
24635  unsigned int fileIndex{};
24636  unsigned int line{};
24637  unsigned int col{};
24638};
24639struct Output {
24640  int type;
24641};
24642class SIMPLECPP_LIB Token {
24643 public:
24644  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
24645      whitespaceahead(wsahead), location(loc), string(s) {
24646      flags();
24647  }
24648  Token(const Token &tok) :
24649      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
24650      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
24651      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
24652  Token &operator=(const Token &tok) = delete;
24653  const TokenString& str() const { return string; }
24654  void setstr(const std::string &s) { string = s; flags(); }
24655  bool isOneOf(const char ops[]) const;
24656  TokenString macro;
24657  char op;
24658  bool comment;
24659  bool name;
24660  bool number;
24661  bool whitespaceahead;
24662  Location location;
24663  Token *previous{};
24664  Token *next{};
24665 private:
24666  void flags() {
24667      name = !string.empty();
24668      comment = false;
24669      number = false;
24670      op = 0;
24671  }
24672  TokenString string;
24673};
24674}
24675struct Following {
24676  int type;
24677};
24678class SIMPLECPP_LIB Later {
24679 public:
24680  Later(int value) : value(value) {}
24681  int value;
24682};
24683"#;
24684        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
24685        assert!(
24686            parsed
24687                .declarations()
24688                .iter()
24689                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
24690        );
24691        assert!(
24692            !parsed
24693                .declarations()
24694                .iter()
24695                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
24696        );
24697        assert!(
24698            parsed
24699                .declarations()
24700                .iter()
24701                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
24702        );
24703        assert!(
24704            !parsed
24705                .declarations()
24706                .iter()
24707                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
24708        );
24709        assert!(
24710            parsed
24711                .declarations()
24712                .iter()
24713                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
24714        );
24715        assert!(
24716            parsed
24717                .declarations()
24718                .iter()
24719                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
24720        );
24721        assert!(
24722            parsed
24723                .declarations()
24724                .iter()
24725                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
24726        );
24727        assert!(
24728            parsed
24729                .declarations()
24730                .iter()
24731                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
24732        );
24733        assert!(
24734            parsed
24735                .declarations()
24736                .iter()
24737                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
24738        );
24739        assert!(
24740            parsed
24741                .declarations()
24742                .iter()
24743                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
24744        );
24745        assert!(parsed.declarations().iter().all(|unit| {
24746            !matches!(
24747                unit.fq_name().as_str(),
24748                "simplecpp.Token.Following" | "simplecpp.Token.Later"
24749            )
24750        }));
24751        assert!(
24752            !parsed
24753                .declarations()
24754                .iter()
24755                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
24756            "the following struct must remain outside the recovered Token class"
24757        );
24758    }
24759
24760    #[test]
24761    #[hardwon(
24762        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24763    )]
24764    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
24765        let source = r#"
24766#define SIMPLECPP_LIB
24767namespace {
24768namespace simplecpp {
24769using TokenString = std::string;
24770struct Location { int line{}; };
24771class SIMPLECPP_LIB HiddenToken {
24772 public:
24773  HiddenToken(const TokenString &s, const Location &loc) :
24774      location(loc), string(s) {
24775      flags();
24776  }
24777  TokenString string;
24778  Location location;
24779  HiddenToken *previous{};
24780 private:
24781  void flags() {}
24782};
24783}
24784}
24785"#;
24786        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
24787        let constructor = parsed
24788            .declarations()
24789            .iter()
24790            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
24791            .expect("recovered anonymous-namespace constructor");
24792        assert_eq!(
24793            parsed
24794                .signature_metadata
24795                .get(constructor)
24796                .and_then(|metadata| metadata.first())
24797                .and_then(SignatureMetadata::callable_linkage),
24798            Some(CallableLinkage::Internal)
24799        );
24800    }
24801
24802    #[test]
24803    #[hardwon(
24804        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24805    )]
24806    fn macro_qualified_static_field_keeps_real_declarator() {
24807        let source = r#"#define JSON_INLINE_VARIABLE
24808struct Reader {
24809static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
24810static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
24811static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
24812};"#;
24813        let mut parser = tree_sitter::Parser::new();
24814        parser
24815            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24816            .unwrap();
24817        let tree = parser.parse(source, None).unwrap();
24818        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
24819        let parsed = parse_cpp_file(&file, source, &tree);
24820        for expected in [
24821            "Reader.npos",
24822            "Reader.other",
24823            "Reader.pointer",
24824            "Reader.reference",
24825        ] {
24826            assert!(
24827                parsed
24828                    .declarations()
24829                    .iter()
24830                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
24831                "real macro-decorated field {expected} is missing: {:#?}",
24832                parsed.declarations()
24833            );
24834        }
24835        assert!(
24836            parsed
24837                .declarations()
24838                .iter()
24839                .all(|unit| unit.fq_name() != "Reader.std"),
24840            "qualified type prefix became a pseudo-field: {:#?}",
24841            parsed.declarations()
24842        );
24843        let root = tree.root_node();
24844        let mut stack = vec![root];
24845        let mut signatures = Vec::new();
24846        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
24847        // `macro_modifier`, so the fields arrive as ordinary clean field
24848        // declarations whose declarators carry the signature text the
24849        // bitfield-clause recovery used to reconstruct.
24850        while let Some(current) = stack.pop() {
24851            if current.kind() == "field_declaration" {
24852                let mut cursor = current.walk();
24853                for declarator in current.children_by_field_name("declarator", &mut cursor) {
24854                    signatures.push(render_cpp_field_signature(current, declarator, source));
24855                }
24856            }
24857            let mut cursor = current.walk();
24858            stack.extend(current.named_children(&mut cursor));
24859        }
24860        signatures.sort();
24861        assert_eq!(
24862            signatures,
24863            [
24864                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
24865                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
24866                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
24867                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
24868            ]
24869        );
24870    }
24871
24872    fn member_function_linkage(source: &str) -> CallableLinkage {
24873        let mut parser = tree_sitter::Parser::new();
24874        parser
24875            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24876            .unwrap();
24877        let tree = parser.parse(source, None).unwrap();
24878        let ancestry = ParentIndex::new(tree.root_node());
24879        let mut stack = vec![tree.root_node()];
24880        while let Some(node) = stack.pop() {
24881            if node.kind() == "function_definition" {
24882                let mut current = node.parent();
24883                while let Some(parent) = current {
24884                    if matches!(
24885                        parent.kind(),
24886                        "class_specifier" | "struct_specifier" | "union_specifier"
24887                    ) {
24888                        return cpp_callable_linkage(node, source, &ancestry);
24889                    }
24890                    current = parent.parent();
24891                }
24892            }
24893            let mut cursor = node.walk();
24894            stack.extend(node.named_children(&mut cursor));
24895        }
24896        panic!("fixture has no member function definition");
24897    }
24898
24899    #[test]
24900    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
24901        assert_eq!(
24902            member_function_linkage("struct Named { int method() { return 1; } };"),
24903            CallableLinkage::External
24904        );
24905        assert_eq!(
24906            member_function_linkage(
24907                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
24908            ),
24909            CallableLinkage::Internal
24910        );
24911        assert_eq!(
24912            member_function_linkage("struct { int method() { return 1; } } instance;"),
24913            CallableLinkage::Internal
24914        );
24915        assert_eq!(
24916            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
24917            CallableLinkage::Internal
24918        );
24919    }
24920
24921    #[test]
24922    #[hardwon(
24923        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24924    )]
24925    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
24926        let source = r#"
24927#ifndef PROTON_VALUE_HPP
24928#define PROTON_VALUE_HPP
24929namespace proton {
24930namespace internal {
24931class value_base {
24932  protected:
24933    internal::data& data();
24934    internal::data data_;
24935  friend class codec::encoder;
24936  friend class codec::decoder;
24937};
24938}
24939class value : public internal::value_base, private internal::comparable<value> {
24940  private:
24941    template<class T, class U=void> struct assignable :
24942        public std::enable_if<codec::is_encodable<T>::value, U> {};
24943    template<class U> struct assignable<value, U> {};
24944  public:
24945    PN_CPP_EXTERN value();
24946    PN_CPP_EXTERN value(const value&);
24947    PN_CPP_EXTERN value& operator=(const value&);
24948    PN_CPP_EXTERN value(value&&);
24949    PN_CPP_EXTERN value& operator=(value&&);
24950    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
24951    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
24952        codec::encoder e(*this);
24953        e << x;
24954        return *this;
24955    }
24956    PN_CPP_EXTERN type_id type() const;
24957    PN_CPP_EXTERN bool empty() const;
24958    PN_CPP_EXTERN void clear();
24959    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
24960    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
24961  friend PN_CPP_EXTERN void swap(value&, value&);
24962  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
24963  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
24964  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
24965    value(pn_data_t* d);
24966    void reset(pn_data_t* d = 0);
24967};
24968}
24969#endif
24970"#;
24971        let mut parser = tree_sitter::Parser::new();
24972        parser
24973            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24974            .unwrap();
24975        let tree = parser.parse(source, None).unwrap();
24976        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
24977        let parsed = parse_cpp_file(&file, source, &tree);
24978        // The macro-declaration grammar parses the class and its decorated
24979        // members cleanly, so the constructors mint with their true
24980        // class-owned identity (`proton.value.value`) rather than the
24981        // namespace-scope identity the fragment recovery produced before.
24982        let macro_constructors = parsed
24983            .signature_metadata
24984            .iter()
24985            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
24986            .flat_map(|(_, metadata)| metadata)
24987            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
24988            .collect::<Vec<_>>();
24989
24990        assert_eq!(
24991            macro_constructors.len(),
24992            3,
24993            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
24994            parsed.declarations()
24995        );
24996        assert!(
24997            macro_constructors.iter().all(|metadata| {
24998                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
24999            }),
25000            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25001        );
25002    }
25003
25004    #[test]
25005    #[hardwon(
25006        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25007    )]
25008    fn recovered_export_class_typedef_uses_displaced_alias_name() {
25009        let source = r#"
25010namespace spi {
25011class Filter {
25012public:
25013    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25014};
25015}
25016namespace filter {
25017class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25018{
25019public:
25020    typedef spi::Filter BASE_CLASS;
25021    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25022    BEGIN_LOG4CXX_CAST_MAP()
25023    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25024    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25025    END_LOG4CXX_CAST_MAP()
25026    FilterDecision decide() const;
25027};
25028}
25029"#;
25030        let mut parser = tree_sitter::Parser::new();
25031        parser
25032            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25033            .unwrap();
25034        let tree = parser.parse(source, None).unwrap();
25035        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25036        let parsed = parse_cpp_file(&file, source, &tree);
25037        assert!(
25038            parsed.declarations().iter().any(|unit| {
25039                unit.is_class()
25040                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25041                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25042            }),
25043            "the displaced typedef alias must retain its declared name: {:#?}",
25044            parsed.declarations()
25045        );
25046        assert!(
25047            parsed
25048                .declarations()
25049                .iter()
25050                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25051            "the qualified underlying type must not become a false nested alias: {:#?}",
25052            parsed.declarations()
25053        );
25054    }
25055
25056    #[test]
25057    #[hardwon(
25058        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25059    )]
25060    fn exported_single_base_recovery_uses_displaced_class_name() {
25061        let source = r#"
25062class CORE_EXPORT QgsPoint : public AbstractGeometry
25063{
25064    Q_GADGET
25065
25066    Q_PROPERTY( double x READ x WRITE setX )
25067    Q_PROPERTY( double y READ y WRITE setY )
25068    Q_PROPERTY( double z READ z WRITE setZ )
25069    Q_PROPERTY( double m READ m WRITE setM )
25070
25071  public:
25072#ifndef SIP_RUN
25073    QgsPoint(
25074      double x = std::numeric_limits<double>::quiet_NaN(),
25075      double y = std::numeric_limits<double>::quiet_NaN(),
25076      double z = std::numeric_limits<double>::quiet_NaN(),
25077      double m = std::numeric_limits<double>::quiet_NaN(),
25078      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25079    );
25080#else
25081    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 )];
25082    % MethodCode
25083    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25084    {
25085      int state;
25086      sipIsErr = 0;
25087      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25088      if ( !sipIsErr )
25089      {
25090        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25091      }
25092      sipReleaseType( p, sipType_QgsPointXY, state );
25093    }
25094    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25095    {
25096      int state;
25097      sipIsErr = 0;
25098
25099      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25100      if ( !sipIsErr )
25101      {
25102        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25103      }
25104      sipReleaseType( p, sipType_QPointF, state );
25105    }
25106    else if (
25107      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25108      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25109      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25110      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25111    {
25112      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25113      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25114      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25115      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25116      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25117      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25118    }
25119    else // Invalid ctor arguments
25120    {
25121      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25122      sipIsErr = 1;
25123    }
25124    % End
25125#endif
25126
25127    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25128    explicit QgsPoint( QPointF p ) SIP_SKIP;
25129    explicit QgsPoint(
25130      Qgis::WkbType wkbType,
25131      double x = std::numeric_limits<double>::quiet_NaN(),
25132      double y = std::numeric_limits<double>::quiet_NaN(),
25133      double z = std::numeric_limits<double>::quiet_NaN(),
25134      double m = std::numeric_limits<double>::quiet_NaN()
25135    ) SIP_SKIP;
25136    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25137    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25138    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25139#ifndef SIP_RUN
25140  private:
25141    bool fuzzyHelper(
25142      double epsilon,
25143      const AbstractGeometry &other,
25144      bool is3DFlag,
25145      bool isMeasureFlag
25146    ) const
25147    {
25148      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25149    }
25150#endif
25151};
25152class Ordinary : public Base { public: Ordinary(); };
25153class API_EXPORT Plain { public: Plain(); };
25154class API_EXPORT : public Base {};
25155class
25156PN_CPP_CLASS_EXTERN Sender : public Link {
25157    Sender();
25158    struct impl;
25159    struct impl& get_impl() const;
25160};
25161class thread_ctx_t {};
25162class ctx_t ZMQ_FINAL : public thread_ctx_t {
25163    bool start();
25164};
25165"#;
25166        let mut parser = tree_sitter::Parser::new();
25167        parser
25168            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25169            .unwrap();
25170        let tree = parser.parse(source, None).unwrap();
25171        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25172        let parsed = parse_cpp_file(&file, source, &tree);
25173        let declarations = parsed.declarations();
25174
25175        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25176            assert!(
25177                declarations
25178                    .iter()
25179                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
25180                "missing recovered class {expected}: {declarations:#?}"
25181            );
25182        }
25183        let qgs_point = declarations
25184            .iter()
25185            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25186            .expect("recovered QgsPoint class");
25187        assert_eq!(
25188            parsed.raw_supertypes.get(qgs_point),
25189            Some(&vec!["AbstractGeometry".to_string()]),
25190            "single-base export recovery must retain its displaced base"
25191        );
25192        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25193        assert!(
25194            parsed
25195                .navigation_ranges
25196                .get(qgs_point)
25197                .is_some_and(|ranges| {
25198                    !ranges.is_empty()
25199                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25200                }),
25201            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25202            parsed.navigation_ranges.get(qgs_point)
25203        );
25204        let sender = declarations
25205            .iter()
25206            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25207            .expect("recovered Sender class");
25208        assert_eq!(
25209            parsed.raw_supertypes.get(sender),
25210            Some(&vec!["Link".to_string()]),
25211            "post-declarator export recovery must retain its displaced base"
25212        );
25213        let recovered_member = declarations
25214            .iter()
25215            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25216            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25217        assert_eq!(
25218            parsed
25219                .signature_metadata
25220                .get(recovered_member)
25221                .and_then(|metadata| metadata.first())
25222                .and_then(SignatureMetadata::callable_linkage),
25223            Some(CallableLinkage::External),
25224            "a named recovered class's members have external linkage"
25225        );
25226        let ctx = declarations
25227            .iter()
25228            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25229            .expect("recovered ctx_t class");
25230        assert_eq!(
25231            parsed.raw_supertypes.get(ctx),
25232            Some(&vec!["thread_ctx_t".to_string()]),
25233            "postfix export-macro recovery must retain its displaced base"
25234        );
25235        assert!(
25236            declarations.iter().any(|unit| {
25237                unit.is_function()
25238                    && unit.fq_name() == "QgsPoint.QgsPoint"
25239                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25240            }),
25241            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25242        );
25243        assert!(
25244            declarations.iter().all(|unit| {
25245                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25246            }),
25247            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25248        );
25249    }
25250
25251    #[test]
25252    #[hardwon(
25253        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25254    )]
25255    fn function_like_export_macro_classes_keep_names_and_base_edges() {
25256        // Every head shape after `class MACRO(2, 0)`: a plain body, a single
25257        // base, `final` without a base, `final` with one base, and `final` with
25258        // a comma-separated base list. Older grammars left each in its own
25259        // error shape; brokk-tree-sitter-cpp 0.24.7 reads them all as
25260        // `class_specifier`s, which publish an empty base list for a class
25261        // without bases.
25262        let source = r#"
25263namespace api {
25264class PROJECT_PUBLIC_API(2, 0) Prelude {
25265  public:
25266    Prelude();
25267};
25268class PROJECT_PUBLIC_API(2, 0) Base {
25269  public:
25270    Base(int value);
25271};
25272class PROJECT_PUBLIC_API(2, 0) Mixin {
25273  public:
25274    Mixin();
25275};
25276class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25277  public:
25278    Adopted(int value);
25279};
25280class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25281  public:
25282    Derived(int value);
25283};
25284class PROJECT_PUBLIC_API(2, 0) Solo final {
25285  public:
25286    Solo();
25287};
25288class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25289  public:
25290    Blended(int value);
25291};
25292class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25293  public:
25294    Woven(int value);
25295};
25296} // namespace api
25297"#;
25298        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25299        let explicit = parse_cpp_declarations(
25300            &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25301            "function-like-export.hpp",
25302        );
25303        assert_eq!(
25304            declaration_expansion_inventory(&parsed),
25305            declaration_expansion_inventory(&explicit)
25306        );
25307        let declarations = parsed.declarations();
25308        let class_named = |name: &str| {
25309            declarations
25310                .iter()
25311                .find(|unit| unit.is_class() && unit.fq_name() == name)
25312                .unwrap_or_else(|| {
25313                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25314                })
25315        };
25316        let base = class_named("api.Base");
25317        class_named("api.Prelude");
25318        class_named("api.Mixin");
25319
25320        assert_eq!(
25321            parsed.raw_supertypes.get(class_named("api.Adopted")),
25322            Some(&vec!["Base".to_string()])
25323        );
25324        assert_eq!(
25325            parsed.raw_supertypes.get(class_named("api.Derived")),
25326            Some(&vec!["Base".to_string()])
25327        );
25328        // The explicit expansion is the oracle for a class without bases: a
25329        // parsed class publishes an empty base list where recovery published
25330        // none.
25331        let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25332        assert_eq!(
25333            solo,
25334            explicit.raw_supertypes.get(class_named("api.Solo")),
25335            "a final class without a base list must match its explicit expansion"
25336        );
25337        assert!(
25338            solo.is_none_or(Vec::is_empty),
25339            "a final class without a base list must not invent a supertype"
25340        );
25341        assert_eq!(
25342            parsed.raw_supertypes.get(class_named("api.Blended")),
25343            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25344        );
25345        assert_eq!(
25346            parsed.raw_supertypes.get(class_named("api.Woven")),
25347            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25348        );
25349        assert!(
25350            declarations
25351                .iter()
25352                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25353            "the export macro must not become a declaration: {declarations:#?}"
25354        );
25355        assert!(
25356            declarations.iter().all(|unit| !matches!(
25357                unit.identifier(),
25358                "final" | "public" | "protected" | "private"
25359            )),
25360            "the head specifiers must not become declarations: {declarations:#?}"
25361        );
25362        assert!(
25363            parsed
25364                .navigation_ranges
25365                .get(base)
25366                .is_some_and(|ranges| !ranges.is_empty()),
25367            "the recovered base must retain a navigable declaration range"
25368        );
25369    }
25370
25371    #[test]
25372    #[hardwon(
25373        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25374    )]
25375    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25376        // #2557: the class name is the last identifier before the head ends
25377        // (`final`, the base clause `:`, or the body), whatever its spelling.
25378        // A class named in capitals (`X509_CA`) is a class, and an object-like
25379        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
25380        // the control whose spelling never mattered.
25381        let source = r#"
25382namespace api {
25383class PROJECT_PUBLIC_API(2, 0) Base {
25384  public:
25385    Base();
25386};
25387class PROJECT_PUBLIC_API(2, 0) Mixin {
25388  public:
25389    Mixin();
25390};
25391class PROJECT_PUBLIC_API(2, 0) Name {
25392  public:
25393    Name();
25394};
25395class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25396  public:
25397    X509_CA();
25398};
25399class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25400  public:
25401    HSS_LMS_KEY();
25402};
25403class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25404  public:
25405    GOST_3410();
25406};
25407class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25408  public:
25409    PKCS11_RSA();
25410};
25411class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25412  public:
25413    Plain();
25414};
25415class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25416  public:
25417    Decorated();
25418};
25419class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25420  public:
25421    Layered();
25422};
25423} // namespace api
25424"#;
25425        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25426        let explicit = parse_cpp_declarations(
25427            &source
25428                .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25429                .replace("FIRST_MACRO SECOND_MACRO ", "")
25430                .replace("OTHER_MACRO ", ""),
25431            "positional-export.hpp",
25432        );
25433        assert_eq!(
25434            declaration_expansion_inventory(&parsed),
25435            declaration_expansion_inventory(&explicit)
25436        );
25437        let declarations = parsed.declarations();
25438        let class_named = |name: &str| {
25439            declarations
25440                .iter()
25441                .find(|unit| unit.is_class() && unit.fq_name() == name)
25442                .unwrap_or_else(|| {
25443                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25444                })
25445        };
25446        for (name, bases) in [
25447            ("api.Name", None),
25448            ("api.X509_CA", None),
25449            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25450            ("api.GOST_3410", Some(vec!["Base"])),
25451            ("api.PKCS11_RSA", None),
25452            ("api.Plain", None),
25453            ("api.Decorated", Some(vec!["Base"])),
25454            ("api.Layered", Some(vec!["Base", "Mixin"])),
25455        ] {
25456            let actual = parsed.raw_supertypes.get(class_named(name));
25457            match bases {
25458                Some(bases) => assert_eq!(
25459                    actual,
25460                    Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25461                    "{name}"
25462                ),
25463                // `MACRO(2, 0) OTHER_MACRO Plain {` without a base still reaches
25464                // recovery, which publishes no base list.
25465                None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25466                // A head the grammar reads publishes the empty base list of
25467                // its explicit expansion.
25468                None => {
25469                    assert_eq!(
25470                        actual,
25471                        explicit.raw_supertypes.get(class_named(name)),
25472                        "{name} must match its explicit expansion"
25473                    );
25474                    assert!(actual.is_none_or(Vec::is_empty), "{name}");
25475                }
25476            }
25477        }
25478        assert!(
25479            declarations.iter().all(|unit| !matches!(
25480                unit.identifier(),
25481                "PROJECT_PUBLIC_API"
25482                    | "OTHER_MACRO"
25483                    | "FIRST_MACRO"
25484                    | "SECOND_MACRO"
25485                    | "final"
25486                    | "public"
25487            )),
25488            "macros and head specifiers must not become declarations: {declarations:#?}"
25489        );
25490    }
25491
25492    #[test]
25493    #[hardwon(
25494        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
25495    )]
25496    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
25497        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
25498        // head, the body's `{` and its first member in one declaration-scope
25499        // ERROR, scatters the remaining members across the container's
25500        // siblings, and ends the container on the class's own `}`. The class
25501        // was lost and the macro minted a class of its own.
25502        let source = r#"
25503namespace api {
25504
25505/**
25506* Doc comment
25507*/
25508class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
25509   public:
25510      /**
25511      * Construct from a point.
25512      */
25513      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
25514
25515#if defined(PROJECT_HAS_LEGACY_POINT)
25516      /**
25517      * Construct from a legacy point.
25518      */
25519      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
25520#endif
25521
25522      std::string algo_name() const override;
25523
25524      AlgorithmIdentifier algorithm_identifier() const override;
25525};
25526
25527}
25528"#;
25529        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
25530        let declarations = parsed.declarations();
25531        let class = declarations
25532            .iter()
25533            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
25534            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25535        assert_eq!(
25536            parsed.raw_supertypes.get(class),
25537            Some(&vec!["BaseKey".to_string()]),
25538            "the virtual base is the class's base: {declarations:#?}"
25539        );
25540        for member in [
25541            "api.VirtualBased.algo_name",
25542            "api.VirtualBased.algorithm_identifier",
25543        ] {
25544            assert!(
25545                declarations
25546                    .iter()
25547                    .any(|unit| unit.is_function() && unit.fq_name() == member),
25548                "{member} must be owned by the recovered class: {declarations:#?}"
25549            );
25550        }
25551        assert!(
25552            declarations
25553                .iter()
25554                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25555            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
25556        );
25557    }
25558
25559    #[test]
25560    #[hardwon(
25561        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
25562    )]
25563    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
25564        // #2924: object-like macro lines before the head demote the `class`
25565        // keyword to a bare identifier and make the macro invocation an
25566        // `init_declarator`, so the head arrives as a `declaration` rather than
25567        // as an ERROR carrying a `class_specifier`. The name is still the last
25568        // identifier before `final`, and `virtual` is a base specifier rather
25569        // than a base.
25570        let source = r#"
25571namespace api {
25572
25573DIAGNOSTIC_PUSH
25574DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
25575
25576class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
25577                                               public virtual api::Inner::Key {
25578   public:
25579      std::string algo_name() const override;
25580};
25581
25582DIAGNOSTIC_POP
25583
25584}
25585"#;
25586        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
25587        let declarations = parsed.declarations();
25588        let class = declarations
25589            .iter()
25590            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
25591            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25592        assert_eq!(
25593            parsed.raw_supertypes.get(class),
25594            Some(&vec![
25595                "api::Outer::Key".to_string(),
25596                "api::Inner::Key".to_string()
25597            ]),
25598            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
25599        );
25600        assert!(
25601            declarations
25602                .iter()
25603                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
25604            "the member is owned by the recovered class: {declarations:#?}"
25605        );
25606        assert!(
25607            declarations
25608                .iter()
25609                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25610            "the macro invocation must not mint a declaration: {declarations:#?}"
25611        );
25612    }
25613
25614    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
25615        format!(
25616            r#"
25617namespace api {{
25618class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
25619   public:
25620      /** Return a descriptive string. */
25621      const char* what() const noexcept override {{ return m_msg.c_str(); }}
25622
25623      /** Return the type of error. */
25624      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
25625
25626      /** Return an associated error code. */
25627      virtual int error_code() const noexcept {{ return 0; }}
25628
25629      /** Avoid throwing the base directly. */
25630      explicit Exception(std::string_view msg);
25631
25632      /** Avoid throwing the base directly. */
25633      Exception(const char* prefix, std::string_view msg);
25634
25635      /** Avoid throwing the base directly. */
25636      Exception(std::string_view msg, const std::exception& e);
25637
25638   private:
25639      std::string m_msg;
25640}};
25641
25642class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
25643   public:
25644      explicit {name}(std::string_view msg);
25645
25646      explicit {name}(std::string_view msg, std::string_view where);
25647
25648      {name}(std::string_view msg, const std::exception& e);
25649
25650      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
25651}};
25652}} // namespace api
25653"#
25654        )
25655    }
25656
25657    #[test]
25658    #[hardwon(
25659        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
25660    )]
25661    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
25662        // The class after a preceding export class follows the same rule
25663        // (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration. Older
25664        // grammars embedded this head in the preceding class's error; since
25665        // brokk-tree-sitter-cpp 0.24.7 the grammar reads both heads, and the
25666        // class must still be named by position.
25667        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
25668            let source = function_like_export_class_fixture(head, name);
25669            let tree = crate::grammar_test_support::tree(&source);
25670            crate::grammar_test_support::assert_function_like_export_class_head(
25671                &tree, &source, name,
25672            );
25673
25674            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
25675            let explicit = parse_cpp_declarations(
25676                &source
25677                    .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25678                    .replace("OTHER_MACRO ", ""),
25679                "embedded-positional-export.hpp",
25680            );
25681            assert_eq!(
25682                declaration_expansion_inventory(&parsed),
25683                declaration_expansion_inventory(&explicit),
25684                "{head}"
25685            );
25686            let declarations = parsed.declarations();
25687            let class = declarations
25688                .iter()
25689                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
25690                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
25691            assert_eq!(
25692                parsed.raw_supertypes.get(class),
25693                Some(&vec!["Exception".to_string()]),
25694                "{head}"
25695            );
25696            assert!(
25697                declarations
25698                    .iter()
25699                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
25700                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
25701            );
25702        }
25703    }
25704
25705    #[test]
25706    #[hardwon(
25707        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
25708    )]
25709    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
25710        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
25711        // `class MACRO(3, 6) Name final :` in the error, the first base as the
25712        // `type` of the following declaration, and closes that declaration with
25713        // a zero-width `MISSING identifier`. Reading the head by position must
25714        // not take that missing node for the class name: an empty name panics
25715        // at `FqName` construction, which took the Botan corpus replay down
25716        // (#2557). The head's tokens straddle the two nodes, so the positional
25717        // read spans both and recovers the class rather than leaving the
25718        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
25719        let source = r#"
25720namespace api {
25721class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
25722                                                    public virtual Botan::EC_PublicKey {
25723   public:
25724      std::string algo_name() const override { return "ECDSA"; }
25725};
25726} // namespace api
25727"#;
25728        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
25729        let declarations = parsed.declarations();
25730        assert!(
25731            declarations
25732                .iter()
25733                .all(|unit| !unit.identifier().is_empty()),
25734            "no declaration may carry an empty name: {declarations:#?}"
25735        );
25736        assert!(
25737            declarations
25738                .iter()
25739                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
25740            "macros and head specifiers must not become declarations: {declarations:#?}"
25741        );
25742        let class = declarations
25743            .iter()
25744            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
25745            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
25746        assert_eq!(
25747            parsed.raw_supertypes.get(class),
25748            Some(&vec![
25749                "Botan::TPM2::PublicKey".to_string(),
25750                "Botan::EC_PublicKey".to_string()
25751            ]),
25752            "both qualified virtual bases are bases: {declarations:#?}"
25753        );
25754        let source_facts = parsed.source_facts.as_ref().unwrap();
25755        let declaration = parsed
25756            .source_declaration_units
25757            .iter()
25758            .find(|(_, unit)| unit == class)
25759            .map(|(declaration, _)| *declaration)
25760            .expect("recovered class source bridge");
25761        let bases = source_facts
25762            .cpp
25763            .as_ref()
25764            .unwrap()
25765            .declarations
25766            .iter()
25767            .find(|fact| fact.declaration == declaration)
25768            .expect("recovered class source fact")
25769            .bases
25770            .clone();
25771        assert_eq!(
25772            bases
25773                .iter()
25774                .map(|base| base.components.clone())
25775                .collect::<Vec<_>>(),
25776            vec![
25777                vec!["Botan", "TPM2", "PublicKey"],
25778                vec!["Botan", "EC_PublicKey"],
25779            ],
25780            "generalized recovery must publish structured base components"
25781        );
25782        assert!(
25783            bases.iter().all(|base| base.is_virtual),
25784            "generalized recovery must preserve virtual base qualifiers"
25785        );
25786        assert!(
25787            declarations
25788                .iter()
25789                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25790            "the head must not mint a macro-named class: {declarations:#?}"
25791        );
25792        let mixed_source = source.replace(
25793            "public virtual Botan::EC_PublicKey",
25794            "public Botan::EC_PublicKey",
25795        );
25796        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
25797        let qualifiers = mixed
25798            .source_facts
25799            .as_ref()
25800            .unwrap()
25801            .cpp
25802            .as_ref()
25803            .unwrap()
25804            .declarations
25805            .iter()
25806            .flat_map(|declaration| &declaration.bases)
25807            .map(|base| base.is_virtual)
25808            .collect::<Vec<_>>();
25809        assert_eq!(
25810            qualifiers,
25811            [true, false],
25812            "base qualifiers must not cross the comma"
25813        );
25814    }
25815
25816    #[test]
25817    #[hardwon(
25818        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25819    )]
25820    fn function_like_export_class_survives_a_preceding_malformed_body() {
25821        // Older grammars left the first class's body malformed and embedded the
25822        // second class in its error. Since brokk-tree-sitter-cpp 0.24.7 both
25823        // heads parse, so both classes are ordinary source declarations.
25824        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
25825        let tree = crate::grammar_test_support::tree(&source);
25826        for name in ["Exception", "Invalid_Argument"] {
25827            crate::grammar_test_support::assert_function_like_export_class_head(
25828                &tree, &source, name,
25829            );
25830        }
25831
25832        let parsed = parse_cpp_file(
25833            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25834            &source,
25835            &tree,
25836        );
25837        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
25838        let explicit = parse_cpp_file(
25839            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
25840            &expanded,
25841            &crate::grammar_test_support::tree(&expanded),
25842        );
25843        assert_eq!(
25844            declaration_expansion_inventory(&parsed),
25845            declaration_expansion_inventory(&explicit)
25846        );
25847        assert!(
25848            !explicit
25849                .materialization_records
25850                .iter()
25851                .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
25852            "the explicit expansion needs no recovery: {:#?}",
25853            explicit.materialization_records
25854        );
25855        let declarations = parsed.declarations();
25856        let exception = declarations
25857            .iter()
25858            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
25859            .expect("qualified-base export class");
25860        let invalid = declarations
25861            .iter()
25862            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
25863            .expect("class embedded in the preceding malformed body");
25864
25865        assert_eq!(
25866            parsed.raw_supertypes.get(exception),
25867            Some(&vec!["std::exception".to_string()])
25868        );
25869        assert_eq!(
25870            parsed.raw_supertypes.get(invalid),
25871            Some(&vec!["Exception".to_string()])
25872        );
25873        assert!(
25874            !parsed.materialization_records.iter().any(|record| matches!(
25875                record,
25876                MaterializationRecord::RecoveredDeclaration { unit, .. }
25877                    if unit == invalid
25878            )),
25879            "a parsed class must not claim recovery provenance: {:#?}",
25880            parsed.materialization_records
25881        );
25882    }
25883
25884    #[test]
25885    #[hardwon(
25886        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25887    )]
25888    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
25889        let source = r#"
25890public:
25891   explicit Lookup_Error(std::string_view err) : Exception(err) {}
25892
25893   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
25894"#;
25895        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
25896            .expect("reparse merged constructor body");
25897        let (range, body) =
25898            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
25899                .unwrap_or_else(|| {
25900                    panic!(
25901                        "the merged constructor must retain its structured declarator/body: {}",
25902                        tree.root_node().to_sexp()
25903                    )
25904                });
25905        assert_eq!(
25906            source.get(range).expect("constructor range"),
25907            "Lookup_Error(std::string_view err) : Exception(err) {}"
25908        );
25909        assert_eq!(node_text(body, source), "{}");
25910    }
25911
25912    #[test]
25913    #[hardwon(
25914        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25915    )]
25916    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
25917        let positive_source =
25918            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
25919        let mut parser = tree_sitter::Parser::new();
25920        parser
25921            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25922            .unwrap();
25923        let positive_tree = parser.parse(positive_source, None).unwrap();
25924        assert!(cpp_reparsed_members_are_indexable(
25925            positive_tree.root_node(),
25926            positive_source
25927        ));
25928
25929        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
25930        let negative_tree = parser.parse(negative_source, None).unwrap();
25931        assert!(!cpp_reparsed_members_are_indexable(
25932            negative_tree.root_node(),
25933            negative_source
25934        ));
25935    }
25936
25937    #[test]
25938    #[hardwon(
25939        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25940    )]
25941    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
25942        let copy_control_source = r#"
25943public:
25944    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
25945    explicit Token(const Token* tok);
25946    ~Token();
25947    Token* astOperand1() { return nullptr; }
25948"#;
25949        let constraint_source = r#"
25950private:
25951    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25952    static T *tokAtImpl(T *tok, int index) {
25953        return tok;
25954    }
25955
25956    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
25957    static T *linkAtImpl(T *tok, int index) {
25958        return tok;
25959    }
25960
25961public:
25962    int late() const { return 1; }
25963"#;
25964        let mut parser = tree_sitter::Parser::new();
25965        parser
25966            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25967            .unwrap();
25968        let copy_control_tree = parser
25969            .parse(copy_control_source, None)
25970            .expect("parse copy-control fixture");
25971        assert!(
25972            copy_control_tree.root_node().has_error(),
25973            "fixture must exercise adjacent copy-control recovery"
25974        );
25975        assert!(
25976            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
25977            "a complete late getter must remain recoverable after adjacent copy-control declarations"
25978        );
25979        let mut cursor = copy_control_tree.root_node().walk();
25980        assert!(
25981            copy_control_tree
25982                .root_node()
25983                .named_children(&mut cursor)
25984                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
25985            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
25986            copy_control_tree.root_node().to_sexp()
25987        );
25988        let constraint_tree = parser
25989            .parse(constraint_source, None)
25990            .expect("parse constraint-macro fixture");
25991        assert!(constraint_tree.root_node().has_error());
25992        assert!(
25993            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
25994            "complete constraint-macro members must not hide a later ordinary member"
25995        );
25996        let mut cursor = constraint_tree.root_node().walk();
25997        assert!(
25998            constraint_tree
25999                .root_node()
26000                .named_children(&mut cursor)
26001                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26002                    child,
26003                    constraint_source
26004                )),
26005            "fixture must retain the split constraint-macro prefix/function geometry"
26006        );
26007    }
26008
26009    #[test]
26010    #[hardwon(
26011        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26012    )]
26013    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26014        let source = r#"
26015struct Analyzer {
26016    struct Action {
26017        Action() = default;
26018        Action(const Action&) = default;
26019        Action& operator=(const Action& rhs) & = default;
26020
26021        template<class T,
26022                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26023                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26024        // NOLINTNEXTLINE(google-explicit-constructor)
26025        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26026        {}
26027
26028        enum : std::uint16_t { None = 0, Read = (1 << 0) };
26029        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26030
26031    private:
26032        unsigned int mFlag{};
26033    };
26034
26035    enum class Direction : unsigned char { Forward, Reverse };
26036    virtual Action analyze(Direction d) const = 0;
26037};
26038"#;
26039        let mut parser = tree_sitter::Parser::new();
26040        parser
26041            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26042            .unwrap();
26043        let tree = parser.parse(source, None).unwrap();
26044        assert!(tree.root_node().has_error());
26045        let root = tree.root_node();
26046        let outer = root
26047            .named_children(&mut root.walk())
26048            .find(|child| child.kind() == "ERROR")
26049            .expect("fragmented Analyzer prefix");
26050        let outer_recovered =
26051            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26052        assert_eq!(outer_recovered.name, "Analyzer");
26053        let outer_tree = cpp_reparse_fragmented_class_body(
26054            source,
26055            outer_recovered.body.reparse_start,
26056            outer_recovered.body.reparse_end,
26057        )
26058        .expect("reparse Analyzer body");
26059        let outer_root = outer_tree.root_node();
26060        let action_prefix = outer_root
26061            .named_children(&mut outer_root.walk())
26062            .find(|child| child.kind() == "ERROR")
26063            .expect("fragmented Action prefix");
26064        let action_recovered = fragmented_class_body(action_prefix, source)
26065            .expect("structured Action fragment boundary");
26066        assert_eq!(action_recovered.name, "Action");
26067        let action_tree = cpp_reparse_fragmented_class_body(
26068            source,
26069            action_recovered.body.reparse_start,
26070            action_recovered.body.reparse_end,
26071        )
26072        .expect("reparse Action body");
26073        let action_root = action_tree.root_node();
26074        let macro_prefix = action_root
26075            .named_children(&mut action_root.walk())
26076            .find(|child| child.kind() == "ERROR")
26077            .expect("constraint macro prefix");
26078        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26079            .expect("structured template macro prefix");
26080        let macro_companion =
26081            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26082        assert!(
26083            cpp_reparsed_template_macro_constructor_companion_is_indexable(
26084                macro_companion,
26085                macro_parameter,
26086                source,
26087            ),
26088            "split constrained constructor must be admitted: {}",
26089            macro_companion.to_sexp()
26090        );
26091        assert!(
26092            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26093            "complete Action body must pass the recovery gate: {}",
26094            action_tree.root_node().to_sexp()
26095        );
26096        assert!(
26097            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26098            "complete Analyzer body must pass the recovery gate: {}",
26099            outer_tree.root_node().to_sexp()
26100        );
26101        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26102        let parsed = parse_cpp_file(&file, source, &tree);
26103        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26104            assert!(
26105                parsed
26106                    .declarations()
26107                    .iter()
26108                    .any(|unit| unit.fq_name() == expected),
26109                "missing recovered declaration {expected}: {:#?}",
26110                parsed.declarations()
26111            );
26112        }
26113        assert!(
26114            parsed
26115                .declarations()
26116                .iter()
26117                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26118            "nested members must not remain flattened: {:#?}",
26119            parsed.declarations()
26120        );
26121    }
26122
26123    #[test]
26124    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26125    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26126        let source = "value() = default;\nint value();\n";
26127        let tree = crate::diagnostics::parse_cpp_tree(source);
26128        let root = tree.root_node();
26129        assert!(
26130            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26131            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26132            root.to_sexp()
26133        );
26134        assert!(
26135            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26136            "a return-typed same-name declaration is not a constructor witness: {}",
26137            root.to_sexp()
26138        );
26139
26140        let damaged_source = r#"
26141value(int x) {
26142  broken(
26143}
26144value() = default;
26145int value();
26146"#;
26147        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26148        let damaged_root = damaged_tree.root_node();
26149        // The macro-declaration grammar absorbs the whole damaged witness
26150        // into one errorful `function_definition`, so the default-assignment
26151        // expression lives inside the bogus body rather than as a top-level
26152        // sibling. The gate's answer must not change: the assignment is not
26153        // the class-named call, and the errorful same-name function is not a
26154        // separate constructor witness.
26155        let mut damaged_stack = vec![damaged_root];
26156        let mut damaged_assignment = None;
26157        while let Some(node) = damaged_stack.pop() {
26158            if node.kind() == "expression_statement" {
26159                damaged_assignment = Some(node);
26160                break;
26161            }
26162            let mut cursor = node.walk();
26163            damaged_stack.extend(node.named_children(&mut cursor));
26164        }
26165        let damaged_assignment =
26166            damaged_assignment.expect("damaged witness probe retains the assignment expression");
26167        let mut damaged_cursor = damaged_root.walk();
26168        let damaged_members = damaged_root
26169            .named_children(&mut damaged_cursor)
26170            .collect::<Vec<_>>();
26171        assert!(
26172            !cpp_reparsed_defaulted_constructor_is_indexable(
26173                damaged_assignment,
26174                &damaged_members,
26175                Some("value"),
26176                damaged_source,
26177            ),
26178            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26179            damaged_root.to_sexp()
26180        );
26181        assert!(
26182            !cpp_reparsed_members_are_indexable_for_class(
26183                damaged_root,
26184                damaged_source,
26185                Some("value")
26186            ),
26187            "the damaged witness must not pass the member gate either: {}",
26188            damaged_root.to_sexp()
26189        );
26190    }
26191
26192    #[test]
26193    #[hardwon(
26194        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26195    )]
26196    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26197        let source = r#"
26198raw_hash_set& operator=(raw_hash_set&& that) {
26199  return move_assign(
26200      std::move(that),
26201      typename AllocTraits::propagate_on_container_move_assignment());
26202}
26203
26204iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26205  return {};
26206}
26207
26208void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26209
26210iterator insert(const_iterator hint, value_type&& value)
26211    ABSL_ATTRIBUTE_LIFETIME_BOUND {
26212  return {};
26213}
26214
26215friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26216  return left.size() == right.size();
26217}
26218
26219static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26220  return static_cast<slot_type*>(buffer);
26221}
26222
26223protected:
26224// Included-range recovery can attach this comment to the template prefix.
26225template <class K>
26226void AssertOnFind([[maybe_unused]] const K& key) {
26227  Check(key);
26228}
26229"#;
26230        let mut parser = tree_sitter::Parser::new();
26231        parser
26232            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26233            .unwrap();
26234        let tree = parser.parse(source, None).unwrap();
26235        assert!(
26236            tree.root_node().has_error(),
26237            "the fixture must exercise tree-sitter's errorful member shapes"
26238        );
26239        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26240
26241        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26242        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26243        assert!(!cpp_reparsed_members_are_indexable(
26244            incomplete_tree.root_node(),
26245            incomplete_source
26246        ));
26247
26248        let outside_error_source = "int foo() stray_attribute {}\n";
26249        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26250        assert!(outside_error_tree.root_node().has_error());
26251        assert!(!cpp_reparsed_members_are_indexable(
26252            outside_error_tree.root_node(),
26253            outside_error_source
26254        ));
26255
26256        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26257        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26258        assert!(!cpp_reparsed_members_are_indexable(
26259            variable_initializer_tree.root_node(),
26260            variable_initializer_source
26261        ));
26262    }
26263
26264    #[test]
26265    #[hardwon(
26266        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26267    )]
26268    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26269        let positive_source = r#"
26270std::pair<iterator, bool> insert(init_type&& value)
26271    ABSL_ATTRIBUTE_LIFETIME_BOUND
26272#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26273  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26274#endif
26275{
26276  return emplace(std::move(value));
26277}
26278"#;
26279        let mut parser = tree_sitter::Parser::new();
26280        parser
26281            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26282            .unwrap();
26283        let positive_tree = parser.parse(positive_source, None).unwrap();
26284        assert!(
26285            positive_tree.root_node().has_error(),
26286            "the fixture must exercise the split attribute/requires shape"
26287        );
26288        assert!(cpp_reparsed_members_are_indexable(
26289            positive_tree.root_node(),
26290            positive_source
26291        ));
26292
26293        let template_return_source = r#"
26294pair<int> insert(init_type&& value)
26295    ABSL_ATTRIBUTE_LIFETIME_BOUND
26296#if LANGUAGE_LEVEL >= 202002L
26297  requires(!Predicate<init_type>::value)
26298#endif
26299// Attributes and the function body may be separated by comments.
26300{
26301  return {};
26302}
26303"#;
26304        let template_return_tree = parser.parse(template_return_source, None).unwrap();
26305        assert!(
26306            cpp_reparsed_members_are_indexable(
26307                template_return_tree.root_node(),
26308                template_return_source
26309            ),
26310            "template-return attribute/requires tree: {}",
26311            template_return_tree.root_node().to_sexp()
26312        );
26313
26314        let no_body_source = r#"
26315std::pair<iterator, bool> insert(init_type&& value)
26316    ABSL_ATTRIBUTE_LIFETIME_BOUND
26317#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26318  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26319#endif
26320+ 0;
26321"#;
26322        let no_body_tree = parser.parse(no_body_source, None).unwrap();
26323        assert!(!cpp_reparsed_members_are_indexable(
26324            no_body_tree.root_node(),
26325            no_body_source
26326        ));
26327
26328        let extra_payload_source = r#"
26329pair<int> insert(init_type&& value)
26330    ABSL_ATTRIBUTE_LIFETIME_BOUND
26331#if LANGUAGE_LEVEL >= 202002L
26332  int unrelated;
26333  requires(Predicate<init_type>::value)
26334#endif
26335{
26336  return {};
26337}
26338"#;
26339        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26340        assert!(!cpp_reparsed_members_are_indexable(
26341            extra_payload_tree.root_node(),
26342            extra_payload_source
26343        ));
26344
26345        let variable_initializer_source = r#"
26346int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26347#if LANGUAGE_LEVEL >= 202002L
26348  requires(true)
26349#endif
26350{
26351  bad;
26352}
26353"#;
26354        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26355        assert!(!cpp_reparsed_members_are_indexable(
26356            variable_initializer_tree.root_node(),
26357            variable_initializer_source
26358        ));
26359    }
26360
26361    #[test]
26362    #[hardwon(
26363        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26364    )]
26365    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26366        let source = r#"namespace absl {
26367ABSL_NAMESPACE_BEGIN namespace container_internal {
26368
26369class raw_hash_set : public Base {
26370 public:
26371  using value_type = int;
26372
26373  template <class U,
26374            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26375  void insert(U value) { (void)value; }
26376
26377  struct InsertSlot {
26378    raw_hash_set& s;
26379  };
26380};
26381
26382}
26383ABSL_NAMESPACE_END
26384}"#;
26385        let mut parser = tree_sitter::Parser::new();
26386        parser
26387            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26388            .unwrap();
26389        let tree = parser.parse(source, None).unwrap();
26390        let root = tree.root_node();
26391        let outer_namespace = root
26392            .named_children(&mut root.walk())
26393            .find(|child| child.kind() == "namespace_definition")
26394            .expect("outer absl namespace");
26395        let declaration_list = outer_namespace
26396            .child_by_field_name("body")
26397            .expect("outer namespace body");
26398        let sentinel_function = declaration_list
26399            .named_children(&mut declaration_list.walk())
26400            .find(|child| child.kind() == "function_definition")
26401            .expect("malformed namespace sentinel function");
26402        let ancestry = ParentIndex::new(root);
26403        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26404            .expect("structured nested namespace sentinel");
26405        let fragmented =
26406            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26407                .expect("fragmented raw_hash_set class");
26408        assert_eq!(fragmented.class_node.kind(), "ERROR");
26409        assert_eq!(fragmented.name, "raw_hash_set");
26410        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26411
26412        let outer_scope =
26413            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26414        let mut outer_siblings = Vec::new();
26415        push_cpp_sentinel_sibling_classes(
26416            &mut outer_siblings,
26417            declaration_list,
26418            sentinel.function,
26419            &outer_scope,
26420            source,
26421            &ancestry,
26422        );
26423        let [outer_shadow] = outer_siblings.as_slice() else {
26424            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26425        };
26426        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26427        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26428
26429        let field = "    raw_hash_set& s;";
26430        let start = source.find(field).expect("InsertSlot field") + 4;
26431        let node = root
26432            .descendant_for_byte_range(start, start + "raw_hash_set".len())
26433            .expect("raw_hash_set type node");
26434        let recovered = cpp_sentinel_recovered_classes(root, source);
26435        let [deep_class] = recovered.as_slice() else {
26436            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26437        };
26438        assert_eq!(
26439            deep_class.namespace_scope_components,
26440            vec!["absl", "container_internal"]
26441        );
26442        assert_eq!(
26443            deep_class.scope_components,
26444            vec!["absl", "container_internal", "raw_hash_set"]
26445        );
26446        assert!(
26447            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26448                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26449        );
26450
26451        assert_eq!(
26452            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26453            Some(vec![
26454                "absl".to_string(),
26455                "container_internal".to_string(),
26456                "raw_hash_set".to_string(),
26457                "InsertSlot".to_string(),
26458            ])
26459        );
26460
26461        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26462        let parsed = parse_cpp_file(&file, source, &tree);
26463        let raw_hash_set = parsed
26464            .declarations()
26465            .iter()
26466            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26467            .expect("recovered raw_hash_set class");
26468        assert_eq!(
26469            raw_hash_set.fq_name(),
26470            "absl::container_internal.raw_hash_set",
26471            "the recovered declaration must publish under the deeper sentinel namespace"
26472        );
26473        assert_eq!(
26474            parsed.raw_supertypes.get(raw_hash_set),
26475            Some(&vec!["Base".to_string()]),
26476            "the structured base clause on the fragmented ERROR prefix must survive publication"
26477        );
26478        assert!(
26479            parsed.materialization_records.iter().any(|record| matches!(
26480                record,
26481                MaterializationRecord::RecoveredDeclaration { recovery, unit }
26482                    if unit == raw_hash_set && *recovery == deep_class.class_range
26483            )),
26484            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26485            parsed.materialization_records
26486        );
26487    }
26488
26489    /// Issue #2358: recording an aggregate definition must not walk the whole
26490    /// file.
26491    ///
26492    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
26493    /// class-like shape that has a body, so the removal step runs once per
26494    /// aggregate. It used to `retain` over `top_level_declarations` and over
26495    /// *every* child list in the file on each of those calls, comparing whole
26496    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
26497    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
26498    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
26499    /// tens of thousands of times over and the C forward differential never
26500    /// finished.
26501    ///
26502    /// A definition the file has not already declared removes nothing, so the
26503    /// honest cost is zero regardless of how many other aggregates surround it.
26504    /// Two sizes an order of magnitude apart pin that the count is not merely
26505    /// small but independent of the file.
26506    ///
26507    /// The declaration walk answers every ancestor question from a
26508    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
26509    /// the root for each one (#2361). Substituting the index is only safe
26510    /// because it answers the identical question, so pin that on the shapes
26511    /// this file's recovery paths care about: anonymous and named aggregates,
26512    /// nested namespaces, templates, macro-displaced declarations and the
26513    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
26514    /// too -- `Node::parent` walks the visible tree, not the named one.
26515    #[test]
26516    #[hardwon(
26517        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
26518    )]
26519    fn the_parent_index_answers_what_tree_sitter_answers() {
26520        const SHAPES: [&str; 5] = [
26521            "namespace outer { namespace inner { struct Tag { int field; }; } }",
26522            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
26523            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
26524            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
26525            "class API Broken : public First, public Second {\n  void member();\n",
26526        ];
26527        for source in SHAPES {
26528            let mut parser = tree_sitter::Parser::new();
26529            parser
26530                .set_language(&tree_sitter_cpp::LANGUAGE.into())
26531                .unwrap();
26532            let tree = parser.parse(source, None).unwrap();
26533            let root = tree.root_node();
26534            let ancestry = ParentIndex::new(root);
26535            let mut nodes = 0usize;
26536            let mut stack = vec![root];
26537            while let Some(node) = stack.pop() {
26538                nodes += 1;
26539                assert_eq!(
26540                    node.parent().map(|parent| parent.id()),
26541                    ancestry.parent(node).map(|parent| parent.id()),
26542                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
26543                );
26544                let mut cursor = node.walk();
26545                stack.extend(node.children(&mut cursor));
26546            }
26547            assert!(nodes > 1, "{source:?} produced no tree to compare");
26548        }
26549    }
26550
26551    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
26552    /// index for every ancestor edge. Asking tree-sitter directly makes each
26553    /// edge re-descend from the root, turning declaration extraction on a
26554    /// deeply nested generated header from quadratic output work into a cubic
26555    /// tree walk. Exact query counts pin the route without a machine-dependent
26556    /// wall-clock ceiling.
26557    #[test]
26558    #[hardwon(
26559        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
26560    )]
26561    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
26562        const DEPTH: usize = 64;
26563        let mut source = String::new();
26564        for level in 0..DEPTH {
26565            writeln!(source, "namespace n{level} {{").unwrap();
26566        }
26567        source.push_str("int deepest(int value);\n");
26568        for _ in 0..DEPTH {
26569            source.push_str("}\n");
26570        }
26571
26572        let mut parser = tree_sitter::Parser::new();
26573        parser
26574            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26575            .unwrap();
26576        let tree = parser.parse(&source, None).unwrap();
26577        let root = tree.root_node();
26578        let ancestry = ParentIndex::new(root);
26579        let mut function_declarator = None;
26580        walk_named_tree_preorder(root, true, |node| {
26581            if node.kind() == "function_declarator" {
26582                function_declarator = Some(node);
26583                WalkControl::Break
26584            } else {
26585                WalkControl::Continue
26586            }
26587        });
26588        let function_declarator = function_declarator.expect("deepest function declarator");
26589        let ancestor_count =
26590            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
26591
26592        ancestry.reset_parent_query_count_for_test();
26593        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
26594        assert_eq!(DEPTH, lexical_scope.len());
26595        assert_eq!(
26596            ancestor_count + 1,
26597            ancestry.parent_query_count_for_test(),
26598            "lexical-scope ancestry bypassed the parent index"
26599        );
26600
26601        ancestry.reset_parent_query_count_for_test();
26602        assert_eq!(
26603            Some(DispatchExtensibility::Closed),
26604            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
26605        );
26606        assert_eq!(
26607            ancestor_count,
26608            ancestry.parent_query_count_for_test(),
26609            "dispatch ancestry bypassed the parent index"
26610        );
26611
26612        ancestry.reset_parent_query_count_for_test();
26613        assert_eq!(
26614            CallableLinkage::External,
26615            cpp_callable_linkage(function_declarator, &source, &ancestry)
26616        );
26617        assert_eq!(
26618            ancestor_count + 1,
26619            ancestry.parent_query_count_for_test(),
26620            "linkage ancestry bypassed the parent index"
26621        );
26622
26623        ancestry.reset_parent_query_count_for_test();
26624        assert!(!cpp_callable_is_structural_constructor(
26625            function_declarator,
26626            &source,
26627            &ancestry
26628        ));
26629        assert_eq!(
26630            ancestor_count + 1,
26631            ancestry.parent_query_count_for_test(),
26632            "constructor ancestry bypassed the parent index"
26633        );
26634    }
26635
26636    /// #3507: the one row-level dispatch fact C++ can state is stated only
26637    /// where the language states it -- in the class body.
26638    ///
26639    /// A call's exactness is not readable from a definition row: an
26640    /// out-of-line definition repeats no `virtual`, no `final`, and no base
26641    /// clause, and C++ inherits virtual-ness from a base that declares the
26642    /// member. Each shape below therefore has to answer for itself, and only
26643    /// a class body with no base clause at all can close a member that writes
26644    /// nothing.
26645    #[test]
26646    #[hardwon(
26647        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
26648    )]
26649    fn callable_dispatch_extensibility_comes_from_the_class_body() {
26650        const SOURCE: &str = "\
26651class Plain { public: long send(long order); };
26652class Derived : public Base { public: long send(long order); };
26653class Sealed : public Base { public: long send(long order) final; };
26654class Hiding : public Base { public: long send(long order) { return order; } };
26655class Virtual { public: virtual long send(long order); };
26656namespace shop { long free_call(long order); }
26657long Plain::send(long order) { return order; }
26658";
26659        let mut parser = tree_sitter::Parser::new();
26660        parser
26661            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26662            .unwrap();
26663        let tree = parser.parse(SOURCE, None).unwrap();
26664        let root = tree.root_node();
26665        let ancestry = ParentIndex::new(root);
26666        let mut answers = Vec::new();
26667        walk_named_tree_preorder(root, true, |node| {
26668            if node.kind() != "function_declarator" {
26669                return WalkControl::Continue;
26670            }
26671            let name = cpp_function_declarator_name_node(node).expect("declarator name");
26672            answers.push((
26673                node_text(name, SOURCE).to_string(),
26674                cpp_callable_dispatch_extensibility(node, &ancestry),
26675            ));
26676            WalkControl::Continue
26677        });
26678        assert_eq!(
26679            answers,
26680            vec![
26681                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26682                ("send".to_string(), Some(DispatchExtensibility::Open)),
26683                ("send".to_string(), Some(DispatchExtensibility::Closed)),
26684                ("send".to_string(), Some(DispatchExtensibility::Open)),
26685                ("send".to_string(), Some(DispatchExtensibility::Open)),
26686                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
26687                ("Plain::send".to_string(), None),
26688            ],
26689            "the class body states the fact; a qualified definition states none"
26690        );
26691    }
26692
26693    #[test]
26694    #[hardwon(
26695        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
26696    )]
26697    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
26698        const DISTINCT_PER_KIND: usize = 64;
26699        let mut source = String::new();
26700        for index in 0..DISTINCT_PER_KIND {
26701            writeln!(source, "typedef int Alias{index};").unwrap();
26702        }
26703        writeln!(source, "typedef long Alias0;").unwrap();
26704        for index in 0..DISTINCT_PER_KIND {
26705            writeln!(source, "#define MACRO_{index} {index}").unwrap();
26706        }
26707        writeln!(source, "#define MACRO_0 duplicate").unwrap();
26708        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
26709
26710        let mut parser = tree_sitter::Parser::new();
26711        parser
26712            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26713            .unwrap();
26714        let tree = parser.parse(&source, None).unwrap();
26715        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
26716
26717        start_declaration_identity_comparison_probe();
26718        let parsed = parse_cpp_file(&file, &source, &tree);
26719        let comparisons = finish_declaration_identity_comparison_probe();
26720
26721        assert_eq!(
26722            DISTINCT_PER_KIND + 1,
26723            parsed
26724                .declarations()
26725                .iter()
26726                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
26727                .count(),
26728            "every physical typedef alias declaration must be retained so \
26729             conditional branch guards stay available to the resolver"
26730        );
26731        assert_eq!(
26732            DISTINCT_PER_KIND + 1,
26733            parsed
26734                .declarations()
26735                .iter()
26736                .filter(|unit| {
26737                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
26738                })
26739                .count(),
26740            "distinct macro redefinitions must remain available to temporal lookup"
26741        );
26742        assert_eq!(
26743            2,
26744            parsed
26745                .declarations()
26746                .iter()
26747                .filter(|unit| {
26748                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
26749                })
26750                .count(),
26751            "function overloads must remain distinct"
26752        );
26753
26754        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
26755        assert!(
26756            comparisons <= dedup_inputs * 4,
26757            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
26758        );
26759    }
26760
26761    #[test]
26762    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
26763    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
26764        let negative_source = r#"namespace absl {
26765ABSL_NAMESPACE_BEGIN namespace container_internal {
26766template <typename T>
26767class broken {
26768 public:
26769  using value_type = T;
26770  T operator->() const { return &operator*(); }
26771}
26772}
26773"#;
26774        let mut parser = tree_sitter::Parser::new();
26775        parser
26776            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26777            .unwrap();
26778        let negative_tree = parser.parse(negative_source, None).unwrap();
26779        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
26780            .expect("the negative fixture must expose the malformed class node");
26781        assert!(
26782            broken.has_error(),
26783            "the negative fixture must retain a parser error"
26784        );
26785        // The macro-declaration grammar keeps the class body closed here; the
26786        // damage is the missing `;` and the unclosed namespace around it, so
26787        // the class's incompleteness is proven by the sentinel recognizers
26788        // finding no envelope for it rather than by a shared parent boundary.
26789        let mut envelope = None;
26790        {
26791            let mut stack = vec![negative_tree.root_node()];
26792            while let Some(node) = stack.pop() {
26793                if cpp_nested_namespace_sentinel(
26794                    node,
26795                    negative_source,
26796                    &ParentIndex::new(negative_tree.root_node()),
26797                )
26798                .is_some()
26799                    || cpp_root_namespace_sentinel(
26800                        node,
26801                        negative_source,
26802                        &ParentIndex::new(negative_tree.root_node()),
26803                    )
26804                    .is_some()
26805                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
26806                {
26807                    envelope = Some(node);
26808                    break;
26809                }
26810                stack.extend(node.named_children(&mut node.walk()));
26811            }
26812        }
26813        assert!(
26814            envelope.is_none(),
26815            "an incomplete class must not prove a sentinel envelope: {}",
26816            negative_tree.root_node().to_sexp()
26817        );
26818        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
26819        assert!(
26820            recovered
26821                .iter()
26822                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
26823            "an incomplete class must not borrow the namespace close: {recovered:#?}"
26824        );
26825
26826        let positive_source = r#"namespace absl {
26827ABSL_NAMESPACE_BEGIN namespace container_internal {
26828template <typename T>
26829class broken {
26830 public:
26831  using value_type = T;
26832  T operator->() const { return &operator*(); }
26833  using alias = value_type;
26834};
26835}
26836}
26837"#;
26838        let positive_tree = parser.parse(positive_source, None).unwrap();
26839        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
26840            .expect("the positive fixture must expose the broken class node");
26841        assert!(
26842            broken.has_error(),
26843            "the positive fixture must retain an internal parser error"
26844        );
26845        assert!(
26846            cpp_complete_class_body_close(broken).is_some(),
26847            "the positive fixture must expose a real class body close"
26848        );
26849        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
26850        assert!(
26851            recovered.iter().any(|class| {
26852                class.scope_components == ["absl", "container_internal", "broken"]
26853            }),
26854            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
26855        );
26856    }
26857
26858    #[test]
26859    #[hardwon(
26860        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
26861    )]
26862    fn sentinel_recovery_keeps_members_after_nested_body_close() {
26863        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
26864NLOHMANN_BASIC_JSON_TPL_DECLARATION
26865class basic_json {
26866 private:
26867  union storage {
26868    int value;
26869  } data;
26870 public:
26871  using late_alias = int;
26872  late_alias value() const;
26873};
26874NLOHMANN_JSON_NAMESPACE_END
26875"#;
26876        let mut parser = tree_sitter::Parser::new();
26877        parser
26878            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26879            .unwrap();
26880        let tree = parser.parse(source, None).unwrap();
26881        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26882        let basic_json = recovered
26883            .iter()
26884            .find(|class| {
26885                class
26886                    .scope_components
26887                    .last()
26888                    .is_some_and(|name| name == "basic_json")
26889            })
26890            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
26891        let late_alias = source
26892            .find("late_alias value")
26893            .expect("late alias reference");
26894        assert!(
26895            basic_json.class_range.start_byte < late_alias
26896                && late_alias < basic_json.class_range.end_byte,
26897            "the recovered class range must include members after a nested close: {basic_json:#?}"
26898        );
26899    }
26900
26901    #[test]
26902    #[hardwon(
26903        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
26904    )]
26905    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
26906        let source = r#"namespace absl {
26907ABSL_NAMESPACE_BEGIN namespace container_internal {
26908template <typename T>
26909struct broken {
26910  using value_type = T;
26911};
26912}
26913
26914#ifdef OWNER_DEF
26915template <typename T>
26916typename broken<T>::value_type broken<T>::method() {
26917  value_type value{};
26918  return value;
26919}
26920#endif
26921
26922namespace sibling {
26923template <typename T>
26924typename broken<T>::value_type broken<T>::other() {
26925  value_type value{};
26926  return value;
26927}
26928}
26929
26930ABSL_NAMESPACE_END
26931}
26932"#;
26933        let mut parser = tree_sitter::Parser::new();
26934        parser
26935            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26936            .unwrap();
26937        let tree = parser.parse(source, None).unwrap();
26938        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
26939        let broken = recovered
26940            .iter()
26941            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
26942            .expect("the sentinel class must be recovered");
26943        let method_start = source
26944            .find("typename broken<T>::value_type broken<T>::method()")
26945            .expect("guarded sibling owner");
26946        let method_end = source[method_start..]
26947            .find("\n}")
26948            .map(|offset| method_start + offset + 2)
26949            .expect("guarded sibling owner close");
26950        assert!(
26951            broken
26952                .owner_ranges
26953                .iter()
26954                .any(|owner| owner.range.start_byte <= method_start
26955                    && method_end <= owner.range.end_byte),
26956            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
26957        );
26958        let sibling_start = source
26959            .find("typename broken<T>::value_type broken<T>::other()")
26960            .expect("nested namespace sibling owner");
26961        assert!(
26962            broken
26963                .owner_ranges
26964                .iter()
26965                .all(|owner| owner.range.start_byte > sibling_start
26966                    || owner.range.end_byte <= sibling_start),
26967            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
26968        );
26969    }
26970
26971    #[test]
26972    #[hardwon(
26973        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
26974    )]
26975    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
26976        let source = r#"#ifdef OUTER
26977namespace absl {
26978ABSL_NAMESPACE_BEGIN namespace container_internal {
26979template <typename T>
26980struct broken {
26981  using value_type = T;
26982};
26983}
26984}
26985
26986#ifdef OWNER_DEF
26987template <typename T>
26988typename broken<T>::value_type broken<T>::method() {
26989  value_type value{};
26990  return value;
26991}
26992#endif
26993#endif
26994"#;
26995        // The macro-declaration grammar keeps the sentinel envelope clean, so
26996        // the recovered-region index has nothing malformed to repair; the
26997        // declaration walk owns the recovery, and the out-of-line definition
26998        // after the missing ABSL_NAMESPACE_END must stay unowned.
26999        let parsed = parse_cpp_declarations(source, "discard-outer.h");
27000        assert!(
27001            parsed
27002                .declarations()
27003                .iter()
27004                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27005            "the sentinel class must be recovered: {:#?}",
27006            parsed.declarations()
27007        );
27008        let method = parsed
27009            .declarations()
27010            .iter()
27011            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27012            .expect("the guarded out-of-line definition");
27013        assert_eq!(
27014            method.fq_name(),
27015            "broken.method",
27016            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27017            parsed.declarations()
27018        );
27019    }
27020
27021    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
27022    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27023        let mut signatures = all_function_identities(parsed)
27024            .into_iter()
27025            .filter(|(name, _)| name == fq_name)
27026            .filter_map(|(_, signature)| signature)
27027            .collect::<Vec<_>>();
27028        signatures.sort();
27029        signatures.dedup();
27030        signatures
27031    }
27032
27033    #[test]
27034    #[hardwon(
27035        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27036    )]
27037    fn callable_parameter_types_come_from_the_ast_parameter_list() {
27038        let source = r#"
27039template <typename T, ENABLE_BYTES(T)>
27040Vec256<T> DupOdd(Vec256<T> value) { return value; }
27041
27042struct Visitor {
27043  void fail(this auto const& self) {}
27044  void consume(this Visitor const& self, int count) {}
27045};
27046"#;
27047        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27048        let dup_odd = parsed
27049            .declarations()
27050            .iter()
27051            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27052            .expect("DupOdd declaration");
27053        assert_eq!(
27054            dup_odd.signature(),
27055            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27056        );
27057        assert_eq!(
27058            parsed
27059                .signature_metadata
27060                .get(dup_odd)
27061                .and_then(|metadata| metadata.first())
27062                .and_then(SignatureMetadata::callable_parameter_types),
27063            Some(["Vec256<T>".to_string()].as_slice())
27064        );
27065
27066        let fail = parsed
27067            .declarations()
27068            .iter()
27069            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27070            .expect("explicit-object member");
27071        assert_eq!(fail.signature(), Some("(this const auto &)"));
27072        let metadata = parsed
27073            .signature_metadata
27074            .get(fail)
27075            .and_then(|metadata| metadata.first())
27076            .expect("explicit-object signature metadata");
27077        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27078        assert!(
27079            metadata
27080                .callable_arity()
27081                .is_some_and(|arity| arity.accepts(0))
27082        );
27083        let consume = parsed
27084            .declarations()
27085            .iter()
27086            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27087            .expect("explicit-object member with an invocation argument");
27088        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27089        let metadata = parsed
27090            .signature_metadata
27091            .get(consume)
27092            .and_then(|metadata| metadata.first())
27093            .expect("typed explicit-object signature metadata");
27094        assert_eq!(
27095            metadata.callable_parameter_types(),
27096            Some(["int".to_string()].as_slice())
27097        );
27098        assert!(
27099            metadata
27100                .callable_arity()
27101                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27102        );
27103    }
27104
27105    #[test]
27106    #[hardwon(
27107        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27108    )]
27109    fn trailing_qualifiers_survive_parameter_list_whitespace() {
27110        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
27111        // declarator's structure, so an out-of-line definition that spells its
27112        // parameter list with different whitespace than the declaration must
27113        // still carry it.
27114        let source = r#"
27115struct Widget {
27116  bool multiline(int settings, int supprs) const;
27117  bool doublespace(int settings, int supprs) const;
27118  bool noexcept_multiline(int settings, int supprs) noexcept;
27119  bool ref_multiline(int settings, int supprs) &&;
27120};
27121bool
27122Widget::multiline (int settings,
27123                   int supprs) const
27124{ return settings + supprs > 0; }
27125bool Widget::doublespace(int settings,  int supprs) const { return true; }
27126bool Widget::noexcept_multiline(int settings,
27127                                int supprs) noexcept { return true; }
27128bool Widget::ref_multiline(int settings,
27129                           int supprs) && { return true; }
27130"#;
27131        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27132        assert_eq!(
27133            vec!["(int, int) const".to_string()],
27134            identity_signatures(&parsed, "Widget.multiline")
27135        );
27136        assert_eq!(
27137            vec!["(int, int) const".to_string()],
27138            identity_signatures(&parsed, "Widget.doublespace")
27139        );
27140        assert_eq!(
27141            vec!["(int, int) noexcept".to_string()],
27142            identity_signatures(&parsed, "Widget.noexcept_multiline")
27143        );
27144        assert_eq!(
27145            vec!["(int, int) &&".to_string()],
27146            identity_signatures(&parsed, "Widget.ref_multiline")
27147        );
27148    }
27149
27150    #[test]
27151    #[hardwon(
27152        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27153    )]
27154    fn macro_fragmented_plain_class_keeps_following_member_signature() {
27155        let source = r#"
27156struct CString {};
27157class CMessage {
27158public:
27159  CString GetParams(unsigned int index, unsigned int length = -1) const
27160      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27161    return GetParamsColon(index, length);
27162  }
27163  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27164};
27165CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27166  return {};
27167}
27168"#;
27169        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27170        assert_eq!(
27171            vec!["(unsigned int, unsigned int) const".to_string()],
27172            identity_signatures(&parsed, "CMessage.GetParamsColon")
27173        );
27174    }
27175
27176    #[test]
27177    #[hardwon(
27178        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27179    )]
27180    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27181        let source = r#"
27182#pragma once
27183#define DEMO_DEPRECATED(message)
27184namespace demo {
27185struct Base {
27186    static int aligned(int value) { return value; }
27187    int legacy(int value) const
27188        DEMO_DEPRECATED("use replacement()") { return value; }
27189    int replacement() const;
27190    void run(int value);
27191};
27192struct OtherBase {
27193    void run(int value);
27194    static int aligned(int value) { return value; }
27195};
27196struct Derived : Base {};
27197struct Override : Base {
27198    void run(int value);
27199    static int aligned(int value) { return value; }
27200};
27201struct RecoveredOverride : Base {
27202    int legacy(int value) const
27203        DEMO_DEPRECATED("use replacement()") { return value; }
27204    void run(int value);
27205};
27206struct Hidden : Base {
27207    void run(int first, int second);
27208    static int aligned(int first, int second) { return first + second; }
27209};
27210struct Ambiguous : Base, OtherBase {};
27211}
27212struct Global {};
27213"#;
27214        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27215        let declarations = parsed.declarations();
27216        let fq_names = declarations
27217            .iter()
27218            .map(|unit| unit.fq_name())
27219            .collect::<std::collections::BTreeSet<_>>();
27220
27221        for expected in [
27222            "demo.Base",
27223            "demo.Base.aligned",
27224            "demo.Base.legacy",
27225            "demo.Base.replacement",
27226            "demo.Base.run",
27227            "demo.Derived",
27228            "demo.OtherBase",
27229            "demo.Override",
27230            "demo.RecoveredOverride",
27231            "demo.Hidden",
27232            "demo.Ambiguous",
27233            "Global",
27234        ] {
27235            assert!(
27236                fq_names.contains(expected),
27237                "missing {expected} from namespaced macro fragment: {declarations:#?}"
27238            );
27239        }
27240        assert!(
27241            !fq_names.contains("Derived"),
27242            "following class escaped its namespace: {declarations:#?}"
27243        );
27244        assert!(
27245            !fq_names.contains("demo.Global"),
27246            "global class crossed the recovered namespace boundary: {declarations:#?}"
27247        );
27248    }
27249
27250    #[test]
27251    #[hardwon(
27252        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27253    )]
27254    fn trailing_qualifiers_still_separate_genuine_overloads() {
27255        // The qualifier must keep distinguishing the real C++ overload sets it
27256        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
27257        let source = r#"
27258struct Widget {
27259  int* slot(int index);
27260  const int* slot(int index) const;
27261  int log(int severity) &;
27262  int log(int severity) &&;
27263};
27264"#;
27265        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27266        assert_eq!(
27267            vec!["(int)".to_string(), "(int) const".to_string()],
27268            identity_signatures(&parsed, "Widget.slot")
27269        );
27270        assert_eq!(
27271            vec!["(int) &".to_string(), "(int) &&".to_string()],
27272            identity_signatures(&parsed, "Widget.log")
27273        );
27274    }
27275
27276    #[test]
27277    #[hardwon(
27278        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27279    )]
27280    fn virtual_specifier_is_not_part_of_the_identity_signature() {
27281        // `override` never appears on the out-of-line definition, and C++ does
27282        // not make it part of the signature, so it must not split the identity.
27283        let source = r#"
27284struct Base {
27285  virtual void run(int value) const;
27286};
27287struct Widget : Base {
27288  void run(int value) const override;
27289};
27290void Widget::run(int value) const {}
27291"#;
27292        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27293        assert_eq!(
27294            vec!["(int) const".to_string()],
27295            identity_signatures(&parsed, "Widget.run")
27296        );
27297    }
27298
27299    #[test]
27300    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27301    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27302        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
27303        // the function type, so a declaration that spells `const int` and a
27304        // definition that spells `int` are one entity.
27305        let source = r#"
27306struct Widget {
27307  bool value_params(const int settings, const int supprs);
27308  void pointee_const(const int* p);
27309  void pointer_const(int* const p);
27310  void both_const(const int* const p);
27311  void reference_const(const int& p);
27312  void array_const(const int values[4]);
27313  void take(const int* p);
27314  void take(int* p);
27315};
27316bool Widget::value_params(int settings, int supprs) { return true; }
27317void Widget::pointer_const(int* p) {}
27318void Widget::both_const(const int* p) {}
27319"#;
27320        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27321        assert_eq!(
27322            vec!["(int, int)".to_string()],
27323            identity_signatures(&parsed, "Widget.value_params")
27324        );
27325        assert_eq!(
27326            vec!["(int *)".to_string()],
27327            identity_signatures(&parsed, "Widget.pointer_const")
27328        );
27329        assert_eq!(
27330            vec!["(const int *)".to_string()],
27331            identity_signatures(&parsed, "Widget.both_const")
27332        );
27333        // The const that is not top-level still distinguishes the type.
27334        assert_eq!(
27335            vec!["(const int *)".to_string()],
27336            identity_signatures(&parsed, "Widget.pointee_const")
27337        );
27338        assert_eq!(
27339            vec!["(const int &)".to_string()],
27340            identity_signatures(&parsed, "Widget.reference_const")
27341        );
27342        assert_eq!(
27343            vec!["(const int [4])".to_string()],
27344            identity_signatures(&parsed, "Widget.array_const")
27345        );
27346        assert_eq!(
27347            vec!["(const int *)".to_string(), "(int *)".to_string()],
27348            identity_signatures(&parsed, "Widget.take")
27349        );
27350    }
27351
27352    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27353        let mut parser = tree_sitter::Parser::new();
27354        parser
27355            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27356            .unwrap();
27357        let tree = parser.parse(source, None).unwrap();
27358        let start = source.find(callable_name).expect("callable declaration");
27359        let declarator =
27360            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27361        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27362    }
27363
27364    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27365        let mut shapes = comparable_shapes(source, callable_name);
27366        assert_eq!(1, shapes.len(), "{shapes:?}");
27367        match shapes.remove(0) {
27368            CppComparableSlot::Shape(shape) => shape,
27369            other => panic!("expected a comparable shape, got {other:?}"),
27370        }
27371    }
27372
27373    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27374        let mut current = shape.root();
27375        loop {
27376            match shape.node(current) {
27377                CppComparableNode::Named { .. } => return shape.node(current),
27378                CppComparableNode::Pointer { inner, .. }
27379                | CppComparableNode::Reference { inner }
27380                | CppComparableNode::Array { inner } => current = *inner,
27381                CppComparableNode::Generic { base, .. } => current = *base,
27382            }
27383        }
27384    }
27385
27386    #[test]
27387    #[hardwon(
27388        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27389    )]
27390    fn comparable_shape_keeps_pointee_const() {
27391        assert_ne!(
27392            sole_comparable_shape("void f(const char* p);", "f("),
27393            sole_comparable_shape("void f(char* p);", "f(")
27394        );
27395    }
27396
27397    #[test]
27398    #[hardwon(
27399        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27400    )]
27401    fn comparable_shape_keeps_inner_pointer_const() {
27402        assert_ne!(
27403            sole_comparable_shape("void f(int** p);", "f("),
27404            sole_comparable_shape("void f(int* const* p);", "f(")
27405        );
27406    }
27407
27408    #[test]
27409    #[hardwon(
27410        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27411    )]
27412    fn comparable_shape_drops_top_level_pointer_const() {
27413        assert_eq!(
27414            sole_comparable_shape("void f(int* const p);", "f("),
27415            sole_comparable_shape("void f(int* p);", "f(")
27416        );
27417    }
27418
27419    #[test]
27420    #[hardwon(
27421        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27422    )]
27423    fn comparable_shape_drops_top_level_base_const() {
27424        assert_eq!(
27425            sole_comparable_shape("void f(const int p);", "f("),
27426            sole_comparable_shape("void f(int p);", "f(")
27427        );
27428    }
27429
27430    #[test]
27431    #[hardwon(
27432        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27433    )]
27434    fn comparable_shape_decays_top_level_array_to_pointer() {
27435        assert_eq!(
27436            sole_comparable_shape("void f(int a[3]);", "f("),
27437            sole_comparable_shape("void f(int* a);", "f(")
27438        );
27439        assert_eq!(
27440            sole_comparable_shape("void f(int* a[3]);", "f("),
27441            sole_comparable_shape("void f(int** a);", "f(")
27442        );
27443    }
27444
27445    #[test]
27446    #[hardwon(
27447        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27448    )]
27449    fn comparable_shape_keeps_array_behind_pointer() {
27450        assert_ne!(
27451            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27452            sole_comparable_shape("struct S { void f(int** a); };", "f(")
27453        );
27454    }
27455
27456    #[test]
27457    #[hardwon(
27458        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27459    )]
27460    fn comparable_shape_records_written_name_and_lexical_scope() {
27461        let declared =
27462            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27463        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27464        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27465            panic!("named leaf");
27466        };
27467        assert_eq!(["Msg".to_string()].as_slice(), name.path());
27468        assert_eq!(
27469            ["ns".to_string(), "S".to_string()].as_slice(),
27470            name.lexical_scope()
27471        );
27472        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27473            panic!("named leaf");
27474        };
27475        assert_eq!(
27476            ["ns".to_string(), "Msg".to_string()].as_slice(),
27477            name.path()
27478        );
27479        assert!(name.lexical_scope().is_empty());
27480        assert_ne!(declared, defined);
27481    }
27482
27483    #[test]
27484    #[hardwon(
27485        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27486    )]
27487    fn comparable_shape_marks_sized_primitive_leaf() {
27488        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27489        let CppComparableNode::Named {
27490            name, primitive, ..
27491        } = comparable_named_leaf(&shape)
27492        else {
27493            panic!("named leaf");
27494        };
27495        assert!(primitive);
27496        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
27497        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
27498    }
27499
27500    #[test]
27501    #[hardwon(
27502        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
27503    )]
27504    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
27505        assert_eq!(
27506            vec![CppComparableSlot::Unstructured],
27507            comparable_shapes("void f(void (*cb)(int));", "f(")
27508        );
27509    }
27510
27511    #[test]
27512    #[hardwon(
27513        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
27514    )]
27515    fn comparable_shape_reports_ellipsis_slot() {
27516        let shapes = comparable_shapes("void f(int a, ...);", "f(");
27517        assert_eq!(2, shapes.len(), "{shapes:?}");
27518        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
27519    }
27520
27521    #[test]
27522    #[hardwon(
27523        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
27524    )]
27525    fn comparable_shape_keeps_template_argument_const() {
27526        assert_ne!(
27527            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
27528            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
27529        );
27530    }
27531
27532    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
27533    /// file-scope tag that a later `struct inner *` at file scope may name.
27534    #[test]
27535    #[hardwon(
27536        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
27537    )]
27538    fn c_file_mints_aggregate_member_tag_at_file_scope() {
27539        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27540        let parsed = parse_cpp_declarations(source, "x.c");
27541        let declarations = parsed.declarations();
27542
27543        assert!(
27544            declarations
27545                .iter()
27546                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27547            "expected a file-scope inner tag, got {declarations:?}"
27548        );
27549        assert!(
27550            declarations
27551                .iter()
27552                .all(|unit| unit.fq_name() != "outer$inner"),
27553            "expected no nested identity, got {declarations:?}"
27554        );
27555        assert!(
27556            declarations
27557                .iter()
27558                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
27559        );
27560        // Members still belong to their own aggregate.
27561        assert!(
27562            declarations
27563                .iter()
27564                .any(|unit| unit.fq_name() == "inner.value")
27565        );
27566        assert!(
27567            declarations
27568                .iter()
27569                .any(|unit| unit.fq_name() == "outer.item")
27570        );
27571
27572        let outer = declarations
27573            .iter()
27574            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
27575            .expect("outer");
27576        assert!(
27577            parsed
27578                .children
27579                .get(outer)
27580                .into_iter()
27581                .flatten()
27582                .all(|child| child.fq_name() != "inner"),
27583            "the tag must not hang off the aggregate it is written inside: {:?}",
27584            parsed.children
27585        );
27586    }
27587
27588    /// A header carries no compilation language of its own, and a `.cpp`
27589    /// translation unit really does declare a nested class. Both keep exactly
27590    /// the C++ extraction they had before the C dialect existed.
27591    #[test]
27592    #[hardwon(
27593        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
27594    )]
27595    fn header_and_cpp_files_keep_nested_tag_identity() {
27596        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27597        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
27598            let parsed = parse_cpp_declarations(source, name);
27599            let declarations = parsed.declarations();
27600            assert!(
27601                declarations
27602                    .iter()
27603                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
27604                "{name} must keep the nested identity, got {declarations:?}"
27605            );
27606            assert!(
27607                declarations.iter().all(|unit| unit.fq_name() != "inner"),
27608                "{name} must not mint a file-scope tag, got {declarations:?}"
27609            );
27610            assert!(
27611                declarations
27612                    .iter()
27613                    .any(|unit| unit.fq_name() == "outer$inner.value")
27614            );
27615        }
27616    }
27617
27618    #[test]
27619    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
27620        let source = r#"
27621template <class K, class V, class Traits = CacheTraits<K>>
27622class ARCCache {
27623 public:
27624    struct ArcSizeInfo {
27625        void BSize() {}
27626    };
27627 private:
27628    struct BMapVal;
27629    struct BListVal;
27630    typedef std::unordered_map<K, BMapVal> BMap;
27631    typedef typename BMap::iterator bmap_iter;
27632    typedef typename std::list<BListVal> BList;
27633    typedef typename BList::iterator blist_iter;
27634    struct BListVal {
27635        bmap_iter map_iter;
27636        BListVal() = default;
27637    };
27638    struct DefinedVal {
27639        DefinedVal() {}
27640    };
27641};
27642"#;
27643        let tree = crate::diagnostics::parse_cpp_tree(source);
27644        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
27645        let cpp = crate::adapter::parse_cpp_file_in_dialect(
27646            &file,
27647            source,
27648            &tree,
27649            LanguageDialect::Standard(Language::Cpp),
27650        );
27651        let c =
27652            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
27653        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
27654            for fq_name in [
27655                "ARCCache$ArcSizeInfo.BSize",
27656                "ARCCache$BListVal.BListVal",
27657                "ARCCache$DefinedVal.DefinedVal",
27658            ] {
27659                assert!(
27660                    parsed
27661                        .declarations()
27662                        .iter()
27663                        .any(|unit| unit.fq_name() == fq_name),
27664                    "{dialect} reading must contain {fq_name}: {:#?}",
27665                    parsed.declarations()
27666                );
27667            }
27668            for flattened in [
27669                "ArcSizeInfo.BSize",
27670                "BListVal.BListVal",
27671                "DefinedVal.DefinedVal",
27672            ] {
27673                assert!(
27674                    parsed
27675                        .declarations()
27676                        .iter()
27677                        .all(|unit| unit.fq_name() != flattened),
27678                    "{dialect} reading must not flatten {flattened}: {:#?}",
27679                    parsed.declarations()
27680                );
27681            }
27682        }
27683    }
27684
27685    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
27686    #[test]
27687    #[hardwon(
27688        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
27689    )]
27690    fn uppercase_c_extension_keeps_cpp_tag_scope() {
27691        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
27692        let parsed = parse_cpp_declarations(source, "x.C");
27693        assert!(
27694            parsed
27695                .declarations()
27696                .iter()
27697                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
27698        );
27699    }
27700
27701    /// There is no such thing as a partially nested tag in C: every level of a
27702    /// nested aggregate chain lands at the same enclosing scope.
27703    #[test]
27704    #[hardwon(
27705        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
27706    )]
27707    fn c_file_mints_every_nesting_level_at_file_scope() {
27708        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
27709        let parsed = parse_cpp_declarations(source, "z.c");
27710        let declarations = parsed.declarations();
27711
27712        for tag in ["a", "b", "c"] {
27713            assert!(
27714                declarations
27715                    .iter()
27716                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
27717                "expected a file-scope {tag}, got {declarations:?}"
27718            );
27719        }
27720        assert!(
27721            declarations
27722                .iter()
27723                .all(|unit| !unit.fq_name().contains('$')),
27724            "no level may keep a nested identity, got {declarations:?}"
27725        );
27726        // Each member still belongs to the aggregate that declares it.
27727        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
27728        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
27729        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
27730    }
27731
27732    /// An enum tag is a tag; its enumerators stay members of the enum, which is
27733    /// what makes them ordinary identifiers at the enum's own (file) scope.
27734    #[test]
27735    #[hardwon(
27736        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
27737    )]
27738    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
27739        let source = "struct outer { enum color { RED, GREEN } c; };\n";
27740        let parsed = parse_cpp_declarations(source, "e.c");
27741        let declarations = parsed.declarations();
27742
27743        let color = declarations
27744            .iter()
27745            .find(|unit| unit.is_class() && unit.fq_name() == "color")
27746            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
27747        assert!(
27748            declarations
27749                .iter()
27750                .all(|unit| unit.fq_name() != "outer$color")
27751        );
27752        for enumerator in ["color.RED", "color.GREEN"] {
27753            assert!(
27754                declarations.iter().any(|unit| unit.fq_name() == enumerator),
27755                "expected {enumerator}, got {declarations:?}"
27756            );
27757        }
27758        let children = parsed
27759            .children
27760            .get(color)
27761            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
27762        assert!(
27763            ["color.RED", "color.GREEN"]
27764                .iter()
27765                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
27766            "enumerators must hang off their enum: {children:?}"
27767        );
27768    }
27769
27770    #[test]
27771    #[hardwon(
27772        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
27773    )]
27774    fn c_file_mints_member_list_union_at_file_scope() {
27775        let source = "struct outer { union inner { int a; float b; } item; };\n";
27776        let parsed = parse_cpp_declarations(source, "u.c");
27777        let declarations = parsed.declarations();
27778        assert!(
27779            declarations
27780                .iter()
27781                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
27782            "expected a file-scope inner union, got {declarations:?}"
27783        );
27784        assert!(
27785            declarations
27786                .iter()
27787                .all(|unit| unit.fq_name() != "outer$inner")
27788        );
27789        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
27790        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
27791    }
27792
27793    /// A tag declared in a namespace member list is not a file-scope tag: the
27794    /// nearest enclosing non-aggregate scope is the namespace.
27795    #[test]
27796    #[hardwon(
27797        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
27798    )]
27799    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
27800        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
27801        let parsed = parse_cpp_declarations(source, "n.c");
27802        let declarations = parsed.declarations();
27803        let inner = declarations
27804            .iter()
27805            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
27806            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
27807        assert_eq!(inner.package_name(), "ns");
27808        assert!(
27809            declarations
27810                .iter()
27811                .all(|unit| unit.fq_name() != "ns.outer$inner")
27812        );
27813    }
27814
27815    /// A tag declared inside a function body: the C reading mints nothing for
27816    /// it (C block scope is a separate capability), while the C++ reading
27817    /// mints the local class under its block identity and its nested class
27818    /// under the local class (#6902). Neither reading flattens either tag into
27819    /// file scope.
27820    #[test]
27821    #[hardwon(
27822        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
27823    )]
27824    fn function_local_tags_keep_block_scope_in_both_dialects() {
27825        let source =
27826            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
27827        for (name, local) in [("y.c", false), ("y.cpp", true)] {
27828            let parsed = parse_cpp_declarations(source, name);
27829            let declarations = parsed.declarations();
27830            assert!(
27831                declarations
27832                    .iter()
27833                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
27834                "{name}: {declarations:?}"
27835            );
27836            for tag in ["localtag", "deeper", "localtag$deeper"] {
27837                assert!(
27838                    declarations.iter().all(|unit| unit.fq_name() != tag),
27839                    "{name} must not mint {tag}, got {declarations:?}"
27840                );
27841            }
27842            for tag in [
27843                "run$block@1:16$localtag",
27844                "run$block@1:16$localtag$deeper",
27845                "run$block@1:16$localtag.d",
27846            ] {
27847                assert_eq!(
27848                    declarations.iter().any(|unit| unit.fq_name() == tag),
27849                    local,
27850                    "{name}: {tag} in {declarations:?}"
27851                );
27852            }
27853        }
27854    }
27855
27856    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
27857    /// re-scope: the typedef name is identical in both dialects.
27858    #[test]
27859    #[hardwon(
27860        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
27861    )]
27862    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
27863        let source = "typedef struct { int v; } T;\n";
27864        for name in ["t.c", "t.cpp"] {
27865            let parsed = parse_cpp_declarations(source, name);
27866            let declarations = parsed.declarations();
27867            assert!(
27868                declarations
27869                    .iter()
27870                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
27871                "{name}: {declarations:?}"
27872            );
27873        }
27874    }
27875
27876    /// The same principle for every anonymous aggregate the walk indexes, not
27877    /// just the typedef one: a promoted anonymous union in a member list, an
27878    /// anonymous struct given a receiver type by its declarator, a
27879    /// function-local aggregate, and one inside an inline member function
27880    /// body. The C++ reading used to mint none of these, so a pure C++
27881    /// workspace indexed no members at all (#3301).
27882    #[test]
27883    #[hardwon(
27884        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
27885    )]
27886    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
27887        let sources = [
27888            "struct outer { union { int a; float b; }; };\n",
27889            "struct outer { struct { int *ops; } sock; };\n",
27890            "void run(void) { struct { int v; } item; }\n",
27891            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
27892        ];
27893        for source in sources {
27894            let names = |name: &str| {
27895                parse_cpp_declarations(source, name)
27896                    .declarations()
27897                    .iter()
27898                    .map(|unit| unit.fq_name())
27899                    .collect::<std::collections::BTreeSet<_>>()
27900            };
27901            let c_names = names("a.c");
27902            let cpp_names = names("a.cpp");
27903            assert_eq!(
27904                c_names, cpp_names,
27905                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
27906            );
27907            assert!(
27908                c_names
27909                    .iter()
27910                    .any(|name| name.contains('.') && name != "outer"),
27911                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
27912            );
27913        }
27914    }
27915
27916    #[test]
27917    #[hardwon(
27918        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
27919    )]
27920    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
27921        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
27922        let parsed = parse_cpp_declarations(source, "socket.c");
27923        let declarations = parsed.declarations();
27924        assert_eq!(
27925            declarations
27926                .iter()
27927                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
27928                .count(),
27929            1,
27930            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
27931        );
27932        for expected in [
27933            "PAL_HANDLE",
27934            "PAL_HANDLE.sock",
27935            "PAL_HANDLE$sock",
27936            "PAL_HANDLE$sock.ops",
27937        ] {
27938            assert!(
27939                declarations.iter().any(|unit| unit.fq_name() == expected),
27940                "expected {expected}, got {declarations:?}"
27941            );
27942        }
27943    }
27944
27945    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
27946    /// every (node, class-like name) pair a tree offers, and require the same
27947    /// answer from both.
27948    ///
27949    /// The release build has no `debug_assertions` agreement check, so this is
27950    /// what pins the two together there.  Both query orders are exercised:
27951    /// document order is what the walk does, and reverse order proves that a
27952    /// question about an earlier byte than one already answered is still
27953    /// filtered back to its own prefix rather than answered from the wider
27954    /// fold.
27955    ///
27956    /// Returns how many questions were answered with a namespace, so a fixture
27957    /// can assert it actually reached the path (#2754).
27958    fn namespace_forward_scan_agreement(source: &str) -> usize {
27959        let mut parser = tree_sitter::Parser::new();
27960        parser
27961            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27962            .unwrap();
27963        let tree = parser.parse(source, None).unwrap();
27964        let root = tree.root_node();
27965        let ancestry = ParentIndex::new(root);
27966
27967        let mut nodes = Vec::new();
27968        let mut names = std::collections::BTreeSet::new();
27969        let mut cursor = root.walk();
27970        let mut stack = vec![root];
27971        while let Some(node) = stack.pop() {
27972            if matches!(
27973                node.kind(),
27974                "class_specifier" | "struct_specifier" | "union_specifier"
27975            ) && let Some(name) = class_like_name(node, source, &ancestry)
27976            {
27977                names.insert(name);
27978            }
27979            nodes.push(node);
27980            stack.extend(node.named_children(&mut cursor));
27981        }
27982        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
27983        assert!(!names.is_empty(), "fixture declares no class-like name");
27984
27985        let mut answered = 0usize;
27986        for reversed in [false, true] {
27987            let mut scan = CppNamespaceForwardScan::default();
27988            let ordered: Vec<_> = if reversed {
27989                nodes.iter().rev().copied().collect()
27990            } else {
27991                nodes.clone()
27992            };
27993            answered = 0;
27994            for node in ordered {
27995                for name in &names {
27996                    scan.advance_to(root, node.start_byte(), source, &ancestry);
27997                    let carried = scan.unique_earlier_forward(name, node);
27998                    assert_eq!(
27999                        carried,
28000                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28001                        "carried-forward scan and prefix scan disagree about {name} at \
28002                         {} node starting at byte {} (reversed order: {reversed})",
28003                        node.kind(),
28004                        node.start_byte()
28005                    );
28006                    answered += usize::from(carried.is_some());
28007                }
28008            }
28009        }
28010        answered
28011    }
28012
28013    /// A malformed namespace whose forward declarations are the only identity
28014    /// signal left for the class definitions tree-sitter pushed out to file
28015    /// scope.  Both recovered classes open the guard; only the first one is
28016    /// separated from the namespace by nothing but recovery trivia, so only the
28017    /// first one borrows.  The carried-forward scan has to reproduce both
28018    /// answers.
28019    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28020namespace ns {
28021class Widget;
28022class Gadget;
28023int x = ;
28024}
28025class API Widget {
28026public:
28027    void first();
28028};
28029class API Gadget {
28030public:
28031    void second();
28032};
28033"#;
28034
28035    #[test]
28036    #[hardwon(
28037        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28038    )]
28039    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28040        assert!(
28041            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28042            "the fixture must actually reach the namespace-borrow path"
28043        );
28044
28045        // Nothing here may be answered, and the two paths have to agree about
28046        // that too: a clean namespace is not an identity proof, two forwards of
28047        // one name are ambiguous rather than a guess, and a forward inside a
28048        // function body is not at namespace scope.
28049        for source in [
28050            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
28051            r#"#define API
28052namespace ns {
28053class Widget;
28054class Widget;
28055int x = ;
28056}
28057class API Widget {
28058public:
28059    void method();
28060};
28061"#,
28062            r#"#define API
28063namespace ns {
28064void host() {
28065    class Widget;
28066}
28067int x = ;
28068}
28069class API Widget {
28070public:
28071    void method();
28072};
28073"#,
28074        ] {
28075            assert_eq!(
28076                namespace_forward_scan_agreement(source),
28077                0,
28078                "no borrow is justified here: {source}"
28079            );
28080        }
28081    }
28082
28083    /// Drive the region reparse and the whitespace-padded reparse it replaced
28084    /// over the same region and require identical trees.
28085    ///
28086    /// The release build has no `debug_assertions` agreement check, so this is
28087    /// what pins the two together there. Each body is reparsed at its own
28088    /// offset and again after a long prefix, because the prefix is the whole
28089    /// difference between the two techniques: the padded parse lexes it as
28090    /// whitespace, the included-range parse never sees it, and the tree has to
28091    /// come out the same either way (#2788).
28092    fn fragmented_class_reparse_agreement(body: &str) {
28093        for prefix in [
28094            String::new(),
28095            "// leading comment\n".to_string(),
28096            // A body starts just after its class head's `{`, which is normally
28097            // mid-line: the padded parse then has spaces before the region on
28098            // the region's own line, and the included-range parse has nothing
28099            // at all before it.
28100            "class Widget : public Base { ".to_string(),
28101            "namespace filler {\n".to_string()
28102                + &"struct Filler { int member; };\n".repeat(200)
28103                + "}\n",
28104            "namespace filler {\n".to_string()
28105                + &"struct Filler { int member; };\n".repeat(200)
28106                + "}\nclass Widget : public Base { ",
28107        ] {
28108            let source = format!("{prefix}{body}");
28109            let start = prefix.len();
28110            let end = source.len();
28111            let region = cpp_reparse_fragmented_class_body(&source, start, end)
28112                .expect("the region reparse must produce a tree");
28113            let padded = cpp_reparse_padded_class_body(&source, start, end)
28114                .expect("the padded reparse must produce a tree");
28115            assert_eq!(
28116                cpp_tree_shape(&region),
28117                cpp_tree_shape(&padded),
28118                "region and padded reparse disagree at offset {start} of {end} bytes"
28119            );
28120            assert_eq!(
28121                region.root_node().start_byte(),
28122                start,
28123                "the reparsed region keeps its original offsets"
28124            );
28125        }
28126    }
28127
28128    #[test]
28129    #[hardwon(
28130        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28131    )]
28132    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28133        // A conditional immediately after an access label: the shape the padded
28134        // technique was kept for, because the directive and its macro name
28135        // become an ERROR plus the following declaration's apparent type.
28136        fragmented_class_reparse_agreement(
28137            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
28138        );
28139        fragmented_class_reparse_agreement(
28140            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
28141        );
28142        // The merged inline constructor and the nested fragmented bodies the
28143        // #938 recovery reads out of a reparse.
28144        fragmented_class_reparse_agreement(
28145            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
28146        );
28147        fragmented_class_reparse_agreement(
28148            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
28149        );
28150        // A body that is not member-shaped at all still has to reparse the same
28151        // way, because the admission gate reads the tree to reject it.
28152        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
28153    }
28154
28155    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28156        let mut siblings = Vec::new();
28157        let mut anchor = node;
28158        while let Some(parent) = anchor.parent() {
28159            let at_translation_unit = parent.kind() == "translation_unit";
28160            let mut sibling = anchor.next_named_sibling();
28161            while let Some(current) = sibling {
28162                if at_translation_unit
28163                    && (current.kind() == "namespace_definition"
28164                        || (current.kind() == "function_definition"
28165                            && first_class_like_child(current).is_some()))
28166                {
28167                    return siblings;
28168                }
28169                siblings.push(current);
28170                if cpp_is_stray_close_brace(current, source) {
28171                    if let Some(semicolon) = current
28172                        .next_named_sibling()
28173                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28174                    {
28175                        siblings.push(semicolon);
28176                    }
28177                    return siblings;
28178                }
28179                if current.start_byte() >= node.end_byte()
28180                    && matches!(current.kind(), "ERROR" | "labeled_statement")
28181                    && cpp_nested_stray_close_brace(current, source).is_some()
28182                {
28183                    return siblings;
28184                }
28185                sibling = current.next_named_sibling();
28186            }
28187            anchor = parent;
28188        }
28189        siblings
28190    }
28191
28192    #[test]
28193    #[hardwon(
28194        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28195    )]
28196    fn following_named_siblings_cursor_matches_native_traversal() {
28197        // A damaged export-class head scatters members across container
28198        // siblings, and the anonymous punctuation forces the cursor to filter
28199        // children exactly as Node::next_named_sibling does. The grammar reads
28200        // a function-like export macro head since brokk-tree-sitter-cpp
28201        // 0.24.7, so the fixture uses an object-like macro before a base
28202        // clause, which still has no parse.
28203        let source = r#"
28204namespace api {
28205class PROJECT_API Widget : public virtual Base {
28206  public:
28207    Widget() {}
28208    void first();
28209};
28210enum After { Value };
28211}
28212"#;
28213        let mut parser = tree_sitter::Parser::new();
28214        parser
28215            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28216            .unwrap();
28217        let tree = parser.parse(source, None).unwrap();
28218        let root = tree.root_node();
28219        assert!(root.has_error(), "fixture must remain damaged");
28220
28221        let ancestry = ParentIndex::new(root);
28222        let mut stack = vec![root];
28223        let mut checked = 0;
28224        let mut damaged = 0;
28225        let mut anonymous = 0;
28226        let mut nonempty_sequences = 0;
28227        let mut named_then_anonymous = 0;
28228        while let Some(node) = stack.pop() {
28229            let expected = native_following_named_siblings(node, source)
28230                .iter()
28231                .map(Node::id)
28232                .collect::<Vec<_>>();
28233            let actual = cpp_following_named_siblings(node, source, &ancestry)
28234                .iter()
28235                .map(Node::id)
28236                .collect::<Vec<_>>();
28237            assert_eq!(
28238                actual,
28239                expected,
28240                "cursor traversal diverged at byte {}",
28241                node.start_byte()
28242            );
28243
28244            let mut cursor = node.walk();
28245            let children = node.children(&mut cursor).collect::<Vec<_>>();
28246            stack.extend(children.iter().copied());
28247            checked += 1;
28248            damaged += usize::from(node.is_error() || node.is_missing());
28249            anonymous += usize::from(!node.is_named());
28250            nonempty_sequences += usize::from(!actual.is_empty());
28251            named_then_anonymous += usize::from(
28252                children
28253                    .windows(2)
28254                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28255            );
28256        }
28257        assert!(checked > 0, "fixture must provide nodes to compare");
28258        assert!(damaged > 0, "fixture must cover damaged nodes");
28259        assert!(anonymous > 0, "fixture must cover anonymous nodes");
28260        assert!(
28261            nonempty_sequences > 0,
28262            "fixture must exercise following named siblings"
28263        );
28264        assert!(
28265            named_then_anonymous > 0,
28266            "fixture must exercise anonymous nodes following a named anchor"
28267        );
28268    }
28269
28270    #[test]
28271    #[hardwon(
28272        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28273    )]
28274    fn a_replacement_removes_owned_enum_fields() {
28275        let source =
28276            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28277        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28278        let mut names: Vec<_> = parsed
28279            .declarations()
28280            .iter()
28281            .map(|unit| unit.fq_name())
28282            .collect();
28283        names.sort();
28284        assert_eq!(
28285            names,
28286            vec![
28287                "Color".to_string(),
28288                "First".to_string(),
28289                "First.A".to_string()
28290            ],
28291            "the replaced Color owns no field any more"
28292        );
28293    }
28294
28295    /// A recovery that re-declares what the file already declared mints
28296    /// nothing.
28297    ///
28298    /// The reparse walk replaces the outer `Widget`, which removes its method,
28299    /// and then re-creates that method from the region. Creation alone would
28300    /// call the method recovered; it was there before the recovery opened, so
28301    /// the recovered set is empty and only the region's own reparse window is
28302    /// recorded (#2787).
28303    #[test]
28304    #[hardwon(
28305        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28306    )]
28307    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28308        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28309                      BEGIN_NS\n\
28310                      namespace demo { struct Widget { void doWork(); }; }\n\
28311                      END_NS\n";
28312        let parsed = parse_cpp_declarations(source, "restored.cpp");
28313        let recovered: Vec<String> = parsed
28314            .materialization_records
28315            .iter()
28316            .filter_map(|record| match record {
28317                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28318                _ => None,
28319            })
28320            .collect();
28321        assert!(
28322            recovered.is_empty(),
28323            "the region declares nothing the file did not already declare: {recovered:?}"
28324        );
28325        let mut names: Vec<String> = parsed
28326            .declarations()
28327            .iter()
28328            .map(|unit| unit.fq_name())
28329            .collect();
28330        names.sort();
28331        assert_eq!(
28332            names,
28333            vec![
28334                "demo".to_string(),
28335                "demo.Widget".to_string(),
28336                "demo.Widget.doWork".to_string(),
28337            ]
28338        );
28339    }
28340
28341    /// Four macro-sentinel recoveries in one file (#941). Each one must record
28342    /// exactly the declarations it minted -- not the ones an earlier recovery
28343    /// minted, and not the file's other declarations -- in start-byte order,
28344    /// and each record must carry its own reparse window (#2787).
28345    #[test]
28346    #[hardwon(
28347        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28348    )]
28349    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28350        let mut source = String::new();
28351        for index in 0..4 {
28352            let _ = write!(
28353                source,
28354                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
28355            );
28356        }
28357        source.push_str("void outside() {}\n");
28358        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28359
28360        let recovered: Vec<(String, (usize, usize))> = parsed
28361            .materialization_records
28362            .iter()
28363            .filter_map(|record| match record {
28364                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28365                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28366                }
28367                _ => None,
28368            })
28369            .collect();
28370
28371        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28372        for index in 0..4 {
28373            // The window the reparse covers: everything after the opening
28374            // sentinel token up to the newline before the closing one.
28375            let region = format!("namespace demo{index}");
28376            let region_start = source.find(&region).expect("each region is in the source");
28377            let start = source[..region_start]
28378                .rfind("BEGIN_NS")
28379                .expect("each region opens with a sentinel")
28380                + "BEGIN_NS".len();
28381            let end = start
28382                + source[start..]
28383                    .find("END_NS")
28384                    .expect("each region closes with a sentinel")
28385                - 1;
28386            let window = (start, end);
28387            for name in [
28388                format!("demo{index}"),
28389                format!("demo{index}.Widget{index}"),
28390                format!("demo{index}.Widget{index}.doWork{index}"),
28391            ] {
28392                expected.push((name, window));
28393            }
28394        }
28395        assert_eq!(
28396            recovered, expected,
28397            "each recovery records its own minted declarations, in order"
28398        );
28399        assert!(
28400            parsed
28401                .declarations()
28402                .iter()
28403                .any(|unit| unit.fq_name() == "outside"),
28404            "the declaration outside every region stays parsed and unrecovered"
28405        );
28406    }
28407
28408    /// `blocks` copies of one plain class-with-methods template. The class the
28409    /// question is about is always the first one, so the only thing that
28410    /// changes between two of these sources is how much unrelated tree
28411    /// surrounds it.
28412    fn repeated_class_blocks(blocks: usize) -> String {
28413        let mut source = String::from("namespace demo {\n");
28414        for index in 0..blocks {
28415            let _ = write!(
28416                source,
28417                "\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"
28418            );
28419        }
28420        source.push_str("\n}\n");
28421        source
28422    }
28423
28424    /// #1496: asking whether a recovered class shape claims one range must cost
28425    /// the path to that range, not a pass over the whole translation unit.
28426    ///
28427    /// The C++ inverse scan asks this once per candidate type reference, so a
28428    /// whole-tree walk makes one file's scan quadratic in its own size. Both
28429    /// sources here answer `None` -- these are plain classes that no recovery
28430    /// shape claims -- which is exactly the case that used to pay full price.
28431    #[test]
28432    #[hardwon(
28433        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28434    )]
28435    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28436        let mut answers = Vec::new();
28437        let mut visits = Vec::new();
28438        let mut node_counts = Vec::new();
28439        for blocks in [200usize, 400] {
28440            let source = repeated_class_blocks(blocks);
28441            let mut parser = tree_sitter::Parser::new();
28442            parser
28443                .set_language(&tree_sitter_cpp::LANGUAGE.into())
28444                .unwrap();
28445            let tree = parser.parse(&source, None).unwrap();
28446            let start_byte = source.find("class Widget0 ").expect("first class");
28447            let end_byte = start_byte
28448                + source[start_byte..]
28449                    .find("};")
28450                    .expect("first class terminator")
28451                + "};".len();
28452            let range = Range {
28453                start_byte,
28454                end_byte,
28455                start_line: 0,
28456                end_line: 0,
28457            };
28458            reset_recovered_class_body_node_visits_for_test();
28459            let recovered_export_classes =
28460                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28461            answers.push(recovered_class_body_at(
28462                &recovered_export_classes,
28463                tree.root_node(),
28464                &source,
28465                "Widget0",
28466                &range,
28467            ));
28468            visits.push(recovered_class_body_node_visits_for_test());
28469            let mut nodes = 0usize;
28470            let mut stack = vec![tree.root_node()];
28471            while let Some(node) = stack.pop() {
28472                nodes += 1;
28473                let mut cursor = node.walk();
28474                stack.extend(node.named_children(&mut cursor));
28475            }
28476            node_counts.push(nodes);
28477        }
28478
28479        assert_eq!(
28480            answers,
28481            vec![None, None],
28482            "no recovered shape claims a plain class"
28483        );
28484        assert_eq!(
28485            visits[0], visits[1],
28486            "the walk must follow the range's own path, so doubling the unrelated \
28487             classes must not change the node count: {visits:?} over trees of \
28488             {node_counts:?} nodes"
28489        );
28490        assert!(
28491            visits[1] * 20 < node_counts[1],
28492            "the walk must stay far below one pass over the tree: {visits:?} over \
28493             trees of {node_counts:?} nodes"
28494        );
28495    }
28496
28497    #[test]
28498    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
28499    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
28500        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
28501        let mut parser = tree_sitter::Parser::new();
28502        parser
28503            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28504            .expect("C++ grammar");
28505        let tree = parser.parse(source, None).expect("fixture tree");
28506        let mut stack = vec![tree.root_node()];
28507        let mut recovered = None;
28508        while let Some(node) = stack.pop() {
28509            if node.kind() == "field_declaration"
28510                && let Some(field) = recovered_function_like_field_declarator(node, source)
28511            {
28512                recovered = Some((node, field.name));
28513                break;
28514            }
28515            let mut cursor = node.walk();
28516            stack.extend(node.named_children(&mut cursor));
28517        }
28518        let (declaration, name) = recovered.unwrap_or_else(|| {
28519            panic!(
28520                "pointer-wrapped macro field was not recovered: {}",
28521                tree.root_node().to_sexp()
28522            )
28523        });
28524        assert_eq!(node_text(name, source), "handshake");
28525        let recovered =
28526            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
28527        assert_eq!(recovered.pointer_depth(), 1);
28528        assert_eq!(
28529            render_cpp_field_signature(declaration, name, source),
28530            "struct handshake * handshake;"
28531        );
28532    }
28533
28534    #[test]
28535    #[hardwon(
28536        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
28537    )]
28538    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
28539        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
28540        let mut parser = tree_sitter::Parser::new();
28541        parser
28542            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28543            .expect("C++ grammar");
28544        let tree = parser.parse(source, None).expect("fixture tree");
28545        // The macro-declaration grammar reads `PyObject_HEAD` as a
28546        // `macro_modifier`, so the member arrives as an ordinary clean field
28547        // declaration rather than the recovery shape the ERROR-based reader
28548        // exists for.
28549        let mut stack = vec![tree.root_node()];
28550        let mut declaration = None;
28551        while let Some(node) = stack.pop() {
28552            if node.kind() == "field_declaration" {
28553                declaration = Some(node);
28554                break;
28555            }
28556            let mut cursor = node.walk();
28557            stack.extend(node.named_children(&mut cursor));
28558        }
28559        let declaration = declaration.unwrap_or_else(|| {
28560            panic!(
28561                "the pointer field must arrive as an ordinary field declaration: {}",
28562                tree.root_node().to_sexp()
28563            )
28564        });
28565        let declarator = declaration
28566            .child_by_field_name("declarator")
28567            .expect("pointer field declarator");
28568        assert_eq!(node_text(declarator, source), "*image");
28569        assert_eq!(
28570            render_cpp_field_signature(declaration, declarator, source),
28571            "PyObject_HEAD ImagingObject * image;",
28572            "the macro marker is retained as modifier provenance, the same \
28573             treatment an attribute or storage-class specifier gets"
28574        );
28575        assert_eq!(
28576            crate::source_facts::capture_field_type(declaration, "image", source),
28577            (
28578                Some(
28579                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
28580                        type_text: "ImagingObject".to_owned(),
28581                        indirection: 1,
28582                        binds_indirectly: true,
28583                        template_arguments: None,
28584                    }
28585                ),
28586                false,
28587            ),
28588            "canonical field facts must use the declared type, not the macro token"
28589        );
28590    }
28591
28592    #[test]
28593    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
28594        let source = "class outer { class inner { int v; }; };\n";
28595        let c_parsed = parse_cpp_declarations(source, "k.c");
28596        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
28597        let c_declarations = c_parsed.declarations();
28598        let cpp_declarations = cpp_parsed.declarations();
28599        assert!(
28600            c_declarations
28601                .iter()
28602                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28603            "{c_declarations:?}"
28604        );
28605        assert_eq!(
28606            c_declarations
28607                .iter()
28608                .map(|unit| unit.fq_name())
28609                .collect::<std::collections::BTreeSet<_>>(),
28610            cpp_declarations
28611                .iter()
28612                .map(|unit| unit.fq_name())
28613                .collect::<std::collections::BTreeSet<_>>()
28614        );
28615    }
28616    #[test]
28617    fn callable_metadata_records_c_receiver_contracts_structurally() {
28618        const SOURCE: &str = concat!(
28619            "static long accumulate(const long *lines, int count) { return count; }\n",
28620            "\n",
28621            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
28622        );
28623        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
28624
28625        assert_eq!(
28626            recorded_callable_modifiers(&parsed),
28627            vec![
28628                "accumulate decl_only=false static=false constructor=false \
28629                 visibility=Unknown"
28630                    .to_string(),
28631                "total decl_only=false static=false constructor=false visibility=Unknown"
28632                    .to_string(),
28633            ],
28634        );
28635    }
28636    #[test]
28637    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
28638        const SOURCE: &str = concat!(
28639            "namespace shop {\n",
28640            "\n",
28641            "class Client {\n",
28642            "public:\n",
28643            "    Client();\n",
28644            "    void send(int order);\n",
28645            "    static int count();\n",
28646            "protected:\n",
28647            "    virtual int audit(int n) const { return n; }\n",
28648            "private:\n",
28649            "    int tally(int n) { return n; }\n",
28650            "};\n",
28651            "\n",
28652            "struct Ledger {\n",
28653            "    void record(int n) { (void)n; }\n",
28654            "};\n",
28655            "\n",
28656            "Client::Client() {}\n",
28657            "void Client::send(int order) { (void)order; }\n",
28658            "int Client::count() { return 0; }\n",
28659            "\n",
28660            "int total(int order) { return order; }\n",
28661            "\n",
28662            "}\n",
28663        );
28664        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
28665
28666        assert_eq!(
28667            recorded_callable_modifiers(&parsed),
28668            vec![
28669                "shop.Client.Client decl_only=false static=false constructor=true \
28670                 visibility=Unknown"
28671                    .to_string(),
28672                "shop.Client.Client decl_only=true static=false constructor=true \
28673                 visibility=Public"
28674                    .to_string(),
28675                "shop.Client.audit decl_only=false static=false constructor=false \
28676                 visibility=Protected"
28677                    .to_string(),
28678                "shop.Client.count decl_only=false static=false constructor=false \
28679                 visibility=Unknown"
28680                    .to_string(),
28681                "shop.Client.count decl_only=true static=true constructor=false \
28682                 visibility=Public"
28683                    .to_string(),
28684                "shop.Client.send decl_only=false static=false constructor=false \
28685                 visibility=Unknown"
28686                    .to_string(),
28687                "shop.Client.send decl_only=true static=false constructor=false \
28688                 visibility=Public"
28689                    .to_string(),
28690                "shop.Client.tally decl_only=false static=false constructor=false \
28691                 visibility=Private"
28692                    .to_string(),
28693                "shop.Ledger.record decl_only=false static=false constructor=false \
28694                 visibility=Public"
28695                    .to_string(),
28696                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
28697                    .to_string(),
28698            ],
28699        );
28700    }
28701
28702    #[test]
28703    fn reparsed_member_signature_grammar_contract() {
28704        use crate::grammar_test_support::{nodes, tree};
28705
28706        // These are class-member fragments reparsed at translation-unit scope,
28707        // exactly the boundary the recovery adapter receives from the grammar.
28708        // Identifier-only annotation errors retain a complete declaration;
28709        // literal payloads and missing signature tokens cannot be admitted.
28710        for (source, expected, error_children, missing_close) in [
28711            ("int f(int n) const { return n; }", true, vec![], false),
28712            (
28713                "int f(int n) const nonneg { return n; }",
28714                true,
28715                vec!["identifier"],
28716                false,
28717            ),
28718            (
28719                "int f(int n) ATTR 42 { return n; }",
28720                false,
28721                vec!["identifier", "number_literal"],
28722                false,
28723            ),
28724            (
28725                "int f(int n = 42 43) { return n; }",
28726                false,
28727                vec!["number_literal"],
28728                false,
28729            ),
28730            ("int f(int n { return n; }", false, vec![], true),
28731        ] {
28732            let parsed = tree(source);
28733            let root = parsed.root_node();
28734            let function = root.named_child(0).unwrap();
28735            assert_eq!(
28736                function.kind(),
28737                "function_definition",
28738                "{source}: {}",
28739                root.to_sexp()
28740            );
28741            let declarator = function.child_by_field_name("declarator").unwrap();
28742            assert_eq!(declarator.kind(), "function_declarator");
28743            let parameters = declarator.child_by_field_name("parameters").unwrap();
28744            assert_eq!(parameters.kind(), "parameter_list");
28745            assert_eq!(
28746                function.child_by_field_name("body").unwrap().kind(),
28747                "compound_statement"
28748            );
28749            let all_nodes = nodes(function);
28750            let errors: Vec<_> = all_nodes
28751                .iter()
28752                .filter(|node| node.kind() == "ERROR")
28753                .collect();
28754            let kinds: Vec<_> = errors
28755                .iter()
28756                .flat_map(|error| {
28757                    error
28758                        .named_children(&mut error.walk())
28759                        .map(|child| child.kind())
28760                        .collect::<Vec<_>>()
28761                })
28762                .collect();
28763            assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
28764            assert_eq!(
28765                all_nodes
28766                    .iter()
28767                    .any(|node| node.is_missing() && node.kind() == ")"),
28768                missing_close,
28769                "{source}"
28770            );
28771            assert_eq!(
28772                cpp_reparsed_members_are_indexable(root, source),
28773                expected,
28774                "{source}: {}",
28775                root.to_sexp()
28776            );
28777        }
28778    }
28779    #[test]
28780    fn qualified_and_template_declarator_name_grammar_contract() {
28781        use crate::grammar_test_support::{nodes, tree};
28782        let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
28783        let parsed = tree(source);
28784        let all = nodes(parsed.root_node());
28785        for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
28786            let qualified = all
28787                .iter()
28788                .copied()
28789                .find(|node| {
28790                    node.kind() == "qualified_identifier" && node_text(*node, source) == written
28791                })
28792                .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
28793            let scope = qualified.child_by_field_name("scope").unwrap();
28794            assert_eq!(node_text(scope, source), owner);
28795            assert!(qualified.child_by_field_name("name").is_some());
28796            assert_eq!(extract_declarator_name(qualified, source), written);
28797        }
28798        let function = all
28799            .iter()
28800            .copied()
28801            .find(|node| {
28802                node.kind() == "function_declarator"
28803                    && node
28804                        .child_by_field_name("declarator")
28805                        .is_some_and(|name| name.kind() == "template_function")
28806            })
28807            .unwrap_or_else(|| {
28808                panic!(
28809                    "missing template declarator: {}",
28810                    parsed.root_node().to_sexp()
28811                )
28812            });
28813        let template = function.child_by_field_name("declarator").unwrap();
28814        let name = template.child_by_field_name("name").unwrap();
28815        assert_eq!(name.kind(), "identifier");
28816        assert_eq!(node_text(name, source), "make");
28817        assert_eq!(
28818            template.child_by_field_name("arguments").unwrap().kind(),
28819            "template_argument_list"
28820        );
28821        assert_eq!(cpp_function_declarator_name_node(function), Some(name));
28822    }
28823
28824    fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
28825        let mut declarations = file
28826            .declarations()
28827            .iter()
28828            .map(|unit| {
28829                let callable = unit.is_function().then(|| {
28830                    let mut labels = file
28831                        .signature_metadata
28832                        .get(unit)
28833                        .into_iter()
28834                        .flatten()
28835                        .map(|metadata| {
28836                            metadata
28837                                .parameters()
28838                                .iter()
28839                                .map(|parameter| parameter.label())
28840                                .collect::<Vec<_>>()
28841                        })
28842                        .collect::<Vec<_>>();
28843                    labels.sort();
28844                    (unit.signature(), labels)
28845                });
28846                let fields = unit.is_field().then(|| {
28847                    let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
28848                    signatures.sort();
28849                    signatures.dedup();
28850                    signatures
28851                });
28852                format!(
28853                    "{:?} {} {fields:?} {callable:?}",
28854                    unit.kind(),
28855                    unit.fq_name()
28856                )
28857            })
28858            .collect::<Vec<_>>();
28859        declarations.sort();
28860        declarations
28861    }
28862
28863    /// Metamorphic oracle: erasing declaration decorators, expanding field-list
28864    /// macros, and spelling a namespace explicitly preserve declaration identity.
28865    /// Compare the complete inventory so a recovered initializer or macro token
28866    /// cannot pass merely because the intended callable was also recovered.
28867    #[test]
28868    fn recovery_declarations_agree_with_explicit_expansions() {
28869        let cases = [
28870            (
28871                "decorated constructors",
28872                "class SIMD_4x26 final { public:\n explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}\n BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28873                "class SIMD_4x26 final { public:\n explicit SIMD_4x26(int v) : m_v(v) {}\n SIMD_4x26() : m_v(0) {}\n int m_v; };\n",
28874            ),
28875            (
28876                "namespace sentinel",
28877                "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
28878                "namespace demo { class Widget { public: int first; void method(); }; }\n",
28879            ),
28880            (
28881                "wrapped prototypes",
28882                "LIBRARY_DEPRECATED(\n LIBRARY_API struct library_context * library_init_from_file(const char * path_model),\n \"use library_init_from_file_with_params instead\"\n);\nLIBRARY_API int next_function(int value);\n",
28883                "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
28884            ),
28885            (
28886                "namespace forward across directives",
28887                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
28888                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
28889            ),
28890            (
28891                "local forward does not lend namespace",
28892                "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
28893                "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
28894            ),
28895            (
28896                "object declarator does not lend namespace",
28897                "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
28898                "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
28899            ),
28900            (
28901                "const template return",
28902                r###"template<class T> struct C { API const T f() { return {}; } };"###,
28903                r###"template<class T> struct C { const T f() { return {}; } };"###,
28904            ),
28905            (
28906                "collapsed struct and union",
28907                r###"#define FIRST int first;
28908#define SECOND int first; int second;
28909union One { FIRST SECOND
28910};
28911struct Two { FIRST SECOND
28912};
28913struct Tail { UNDEFINED
28914};
28915"###,
28916                r###"#define FIRST int first;
28917#define SECOND int first; int second;
28918union One { int first; int first; int second;
28919};
28920struct Two { int first; int first; int second;
28921};
28922struct Tail { UNDEFINED
28923};
28924"###,
28925            ),
28926            (
28927                "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
28928                r###"
28929#pragma once
28930#define DEMO_DEPRECATED(message)
28931namespace demo {
28932struct Base {
28933    static int aligned(int value) { return value; }
28934    int legacy(int value) const
28935        DEMO_DEPRECATED("use replacement()") { return value; }
28936    int replacement() const;
28937    void run(int value);
28938};
28939struct OtherBase {
28940    void run(int value);
28941    static int aligned(int value) { return value; }
28942};
28943struct Derived : Base {};
28944struct Override : Base {
28945    void run(int value);
28946    static int aligned(int value) { return value; }
28947};
28948struct RecoveredOverride : Base {
28949    int legacy(int value) const
28950        DEMO_DEPRECATED("use replacement()") { return value; }
28951    void run(int value);
28952};
28953struct Hidden : Base {
28954    void run(int first, int second);
28955    static int aligned(int first, int second) { return first + second; }
28956};
28957struct Ambiguous : Base, OtherBase {};
28958}
28959struct Global {};
28960"###,
28961                r###"
28962#pragma once
28963#define DEMO_DEPRECATED(message)
28964namespace demo {
28965struct Base {
28966    static int aligned(int value) { return value; }
28967    int legacy(int value) const
28968         { return value; }
28969    int replacement() const;
28970    void run(int value);
28971};
28972struct OtherBase {
28973    void run(int value);
28974    static int aligned(int value) { return value; }
28975};
28976struct Derived : Base {};
28977struct Override : Base {
28978    void run(int value);
28979    static int aligned(int value) { return value; }
28980};
28981struct RecoveredOverride : Base {
28982    int legacy(int value) const
28983         { return value; }
28984    void run(int value);
28985};
28986struct Hidden : Base {
28987    void run(int first, int second);
28988    static int aligned(int first, int second) { return first + second; }
28989};
28990struct Ambiguous : Base, OtherBase {};
28991}
28992struct Global {};
28993"###,
28994            ),
28995            (
28996                "sentinel union",
28997                r###"BEGIN_NS
28998union Storage {int value; char byte;};
28999END_NS
29000"###,
29001                r###"union Storage {int value; char byte;};
29002"###,
29003            ),
29004            (
29005                "compact sentinel class",
29006                r###"BEGIN_NS
29007class Widget {int first; void method();};
29008END_NS
29009"###,
29010                r###"class Widget {int first; void method();};
29011"###,
29012            ),
29013            (
29014                "three decorators",
29015                "class C { FIRST SECOND THIRD void f() {} };",
29016                "class C { void f() {} };",
29017            ),
29018            (
29019                "overlapping field lists",
29020                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29021                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29022            ),
29023            (
29024                "identical redefinition",
29025                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29026                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29027            ),
29028            (
29029                "unconditional reset",
29030                "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { FIELDS int tail;\n};\n",
29031                "#if MODE\n#define FIELDS int left;\n#else\n#define FIELDS int right;\n#endif\n#undef FIELDS\n#define FIELDS int restored;\nstruct Handle { int restored; int tail;\n};\n",
29032            ),
29033        ];
29034        for (label, source_lf, expanded_lf) in cases {
29035            for (source, expanded) in [
29036                (source_lf.to_owned(), expanded_lf.to_owned()),
29037                (
29038                    source_lf.replace('\n', "\r\n"),
29039                    expanded_lf.replace('\n', "\r\n"),
29040                ),
29041            ] {
29042                let tree = crate::grammar_test_support::tree(&source);
29043                let nodes = crate::grammar_test_support::nodes(tree.root_node());
29044                match label {
29045                    "decorated constructors" => assert!(
29046                        nodes.iter().any(|node| {
29047                            node.kind() == "ERROR"
29048                                && node
29049                                    .named_child(0)
29050                                    .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29051                        }),
29052                        "{}",
29053                        tree.root_node().to_sexp()
29054                    ),
29055                    "wrapped prototypes" => assert!(
29056                        tree.root_node().named_child(0).is_some_and(|node| {
29057                            node.kind() == "ERROR"
29058                                && node
29059                                    .named_child(0)
29060                                    .is_some_and(|head| head.kind() == "function_declarator")
29061                        }),
29062                        "{}",
29063                        tree.root_node().to_sexp()
29064                    ),
29065                    "collapsed struct and union" => assert!(
29066                        nodes.iter().any(|node| {
29067                            node.kind() == "union"
29068                                && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29069                        }),
29070                        "{}",
29071                        tree.root_node().to_sexp()
29072                    ),
29073                    "object declarator does not lend namespace" => assert!(
29074                        nodes
29075                            .iter()
29076                            .any(|node| { node.kind() == "parenthesized_declarator" }),
29077                        "{}",
29078                        tree.root_node().to_sexp()
29079                    ),
29080                    _ => {}
29081                }
29082                let parsed = parse_cpp_declarations(&source, "expansion.h");
29083                let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29084                assert_eq!(
29085                    declaration_expansion_inventory(&parsed),
29086                    declaration_expansion_inventory(&explicit),
29087                    "{label}"
29088                );
29089                for unit in parsed.declarations() {
29090                    for range in parsed.declaration_ranges(unit) {
29091                        assert!(
29092                            range.start_byte < range.end_byte,
29093                            "{label}: {unit:?}: {range:?}"
29094                        );
29095                        for (offset, line) in [
29096                            (range.start_byte, range.start_line),
29097                            (range.end_byte, range.end_line),
29098                        ] {
29099                            assert_eq!(
29100                                line,
29101                                source[..offset]
29102                                    .bytes()
29103                                    .filter(|byte| *byte == b'\n')
29104                                    .count()
29105                                    + 1,
29106                                "{label}: {unit:?}: {range:?}"
29107                            );
29108                        }
29109                    }
29110                }
29111            }
29112        }
29113    }
29114
29115    #[test]
29116    #[hardwon(
29117        "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29118    )]
29119    fn recovered_class_constructor_agrees_with_explicit_expansion() {
29120        let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29121        let tree = crate::grammar_test_support::tree(source);
29122        crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29123        let actual = parse_cpp_declarations(source, "constructor.h");
29124        let explicit = parse_cpp_declarations(
29125            "class value { public: value(pn_data_t* d); int following(); };",
29126            "constructor.h",
29127        );
29128        assert_eq!(function_identities(&actual), function_identities(&explicit));
29129    }
29130
29131    #[test]
29132    #[hardwon(
29133        "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29134    )]
29135    fn recovered_base_initializer_is_not_a_declaration() {
29136        let source = r###"
29137namespace api {
29138
29139/**
29140* Doc comment
29141*/
29142class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29143   public:
29144      /**
29145      * Construct from a point.
29146      */
29147      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29148
29149#if defined(PROJECT_HAS_LEGACY_POINT)
29150      /**
29151      * Construct from a legacy point.
29152      */
29153      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29154#endif
29155
29156      std::string algo_name() const override;
29157
29158      AlgorithmIdentifier algorithm_identifier() const override;
29159};
29160
29161}
29162"###;
29163        let tree = crate::grammar_test_support::tree(source);
29164        crate::grammar_test_support::assert_function_like_export_class_head(
29165            &tree,
29166            source,
29167            "VirtualBased",
29168        );
29169        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29170        let actual = parse_cpp_declarations(source, "virtual-base.h");
29171        let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29172        assert_eq!(
29173            declaration_expansion_inventory(&actual),
29174            declaration_expansion_inventory(&explicit)
29175        );
29176    }
29177
29178    #[test]
29179    #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29180    fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29181        let source = r###"struct Aggregate;
29182
29183#define NOARG
29184LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29185#undef NOARG
29186
29187struct Context { int value; };
29188struct Visit { int value; };
29189
29190LIST_DECLS(static, deps, struct Aggregate *, unused)
29191LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29192
29193static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29194
29195static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29196static struct Context *restore(struct Aggregate *value) {
29197  return (struct Context *) value;
29198}
29199
29200static void caller(struct Context *ctx, struct Visit *visit) {
29201  target(ctx, 0, 0, visit); /* recovered prototype */
29202}
29203
29204static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29205  (void) ctx;
29206  (void) first;
29207  (void) second;
29208  (void) visit;
29209}
29210"###;
29211        let expanded = r###"struct Aggregate;
29212
29213#define NOARG
29214#undef NOARG
29215
29216struct Context { int value; };
29217struct Visit { int value; };
29218
29219
29220static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29221
29222static struct Context *restore(struct Aggregate *value);
29223static struct Context *restore(struct Aggregate *value) {
29224  return (struct Context *) value;
29225}
29226
29227static void caller(struct Context *ctx, struct Visit *visit) {
29228  target(ctx, 0, 0, visit); /* recovered prototype */
29229}
29230
29231static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29232  (void) ctx;
29233  (void) first;
29234  (void) second;
29235  (void) visit;
29236}
29237"###;
29238        let tree = crate::grammar_test_support::tree(source);
29239        assert!(
29240            tree.root_node().has_error(),
29241            "{}",
29242            tree.root_node().to_sexp()
29243        );
29244        let actual = parse_cpp_declarations(source, "expansion.h");
29245        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29246        assert_eq!(
29247            declaration_expansion_inventory(&actual),
29248            declaration_expansion_inventory(&explicit)
29249        );
29250    }
29251
29252    #[test]
29253    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29254    fn recovered_sentinel_alias_agrees_with_expansion() {
29255        let source = r###"namespace absl {
29256ABSL_NAMESPACE_BEGIN namespace container_internal {
29257
29258class raw_hash_set : public Base {
29259 public:
29260  using value_type = int;
29261
29262  template <class U,
29263            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29264  void insert(U value) { (void)value; }
29265
29266  struct InsertSlot {
29267    raw_hash_set& s;
29268  };
29269};
29270
29271}
29272ABSL_NAMESPACE_END
29273}"###;
29274        let expanded = r###"namespace absl {
29275namespace container_internal {
29276
29277class raw_hash_set : public Base {
29278 public:
29279  using value_type = int;
29280
29281  template <class U>
29282  void insert(U value) { (void)value; }
29283
29284  struct InsertSlot {
29285    raw_hash_set& s;
29286  };
29287};
29288
29289}
29290
29291}"###;
29292        let tree = crate::grammar_test_support::tree(source);
29293        assert!(
29294            tree.root_node().has_error(),
29295            "{}",
29296            tree.root_node().to_sexp()
29297        );
29298        let actual = parse_cpp_declarations(source, "expansion.h");
29299        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29300        assert_eq!(
29301            declaration_expansion_inventory(&actual),
29302            declaration_expansion_inventory(&explicit)
29303        );
29304    }
29305
29306    /// The structured alias facts the resolver and navigation read, for one
29307    /// declaration of `fq_name`.
29308    fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29309        let units = file
29310            .declarations()
29311            .iter()
29312            .filter(|unit| unit.fq_name() == fq_name)
29313            .collect::<Vec<_>>();
29314        let [unit] = units.as_slice() else {
29315            panic!("expected one {fq_name}: {:#?}", file.declarations());
29316        };
29317        let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29318        let fact = file
29319            .source_declaration_units
29320            .iter()
29321            .filter(|(_, linked)| linked == *unit)
29322            .map(|(declaration, _)| {
29323                facts
29324                    .declarations
29325                    .iter()
29326                    .find(|fact| fact.declaration == *declaration)
29327                    .expect("alias source fact")
29328            })
29329            .map(|fact| {
29330                (
29331                    &fact.alias_target,
29332                    &fact.alias_target_text,
29333                    &fact.lexical_path,
29334                    &fact.guard_requirements,
29335                    &fact.file_scope_alias,
29336                )
29337            })
29338            .collect::<Vec<_>>();
29339        let parents = file
29340            .children
29341            .iter()
29342            .filter(|(_, children)| children.contains(unit))
29343            .map(|(parent, _)| parent.fq_name())
29344            .collect::<Vec<_>>();
29345        format!(
29346            "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29347            unit.kind(),
29348            file.type_aliases.contains(unit),
29349            file.signatures.get(unit),
29350            file.signature_metadata.get(unit),
29351            file.cpp_template_metadata.get(unit),
29352        )
29353    }
29354
29355    #[test]
29356    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29357    fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29358        let source = r###"namespace absl {
29359ABSL_NAMESPACE_BEGIN namespace container_internal {
29360
29361class raw_hash_set : public Base {
29362 public:
29363  using value_type = int;
29364  template <class T>
29365  using rebind = Base<T>;
29366  using key_type = value_type;
29367#ifdef ABSL_HAVE_SLOT
29368  using slot_type = long;
29369#endif
29370
29371  template <class U,
29372            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29373  void insert(U value) { (void)value; }
29374
29375  struct InsertSlot {
29376    raw_hash_set& s;
29377  };
29378};
29379
29380}
29381ABSL_NAMESPACE_END
29382}"###;
29383        let expanded = source
29384            .replace("ABSL_NAMESPACE_BEGIN ", "")
29385            .replace("ABSL_NAMESPACE_END", "")
29386            .replace(
29387                ",\n            REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29388                ">",
29389            );
29390        assert!(
29391            crate::grammar_test_support::tree(source)
29392                .root_node()
29393                .has_error()
29394        );
29395        assert!(
29396            !crate::grammar_test_support::tree(&expanded)
29397                .root_node()
29398                .has_error()
29399        );
29400        let actual = parse_cpp_declarations(source, "expansion.h");
29401        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29402        assert_eq!(
29403            declaration_expansion_inventory(&actual),
29404            declaration_expansion_inventory(&explicit)
29405        );
29406        for (alias, text) in [
29407            ("value_type", "using value_type = int;"),
29408            ("rebind", "using rebind = Base<T>;"),
29409            ("key_type", "using key_type = value_type;"),
29410            ("slot_type", "using slot_type = long;"),
29411        ] {
29412            let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29413            // Kind, owner, target, template head, guard, and lexical scope all
29414            // agree with the explicitly written class.
29415            assert_eq!(
29416                sentinel_alias_facts(&actual, &fq_name),
29417                sentinel_alias_facts(&explicit, &fq_name),
29418                "{alias}"
29419            );
29420            // The range is the alias's own source text, line included.
29421            let unit = actual
29422                .declarations()
29423                .iter()
29424                .find(|unit| unit.fq_name() == fq_name)
29425                .unwrap();
29426            let start = source.find(text).unwrap();
29427            assert_eq!(
29428                actual.declaration_ranges(unit),
29429                [Range {
29430                    start_byte: start,
29431                    end_byte: start + text.len(),
29432                    start_line: source[..start].matches('\n').count() + 1,
29433                    end_line: source[..start].matches('\n').count() + 1,
29434                }],
29435                "{alias}"
29436            );
29437        }
29438        let rendered =
29439            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29440        assert!(
29441            rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29442                && rendered
29443                    .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29444            "the target resolves in the recovered class: {rendered}"
29445        );
29446        assert!(
29447            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29448                .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29449            "the guarded alias keeps its guard"
29450        );
29451    }
29452
29453    #[test]
29454    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29455    fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29456        // Without the class's displaced `};` the sentinel recovery cannot
29457        // prove where raw_hash_set ends, so it recovers no class at all.
29458        let source = r###"namespace absl {
29459ABSL_NAMESPACE_BEGIN namespace container_internal {
29460
29461class raw_hash_set : public Base {
29462 public:
29463  using value_type = int;
29464
29465  template <class U,
29466            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29467  void insert(U value) { (void)value; }
29468
29469}
29470ABSL_NAMESPACE_END
29471}"###;
29472        let parsed = parse_cpp_declarations(source, "unproven.h");
29473        let declarations = parsed.declarations();
29474        assert!(
29475            declarations
29476                .iter()
29477                .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29478            "no member may claim an owner the recovery did not prove: {declarations:#?}"
29479        );
29480        assert!(
29481            declarations
29482                .iter()
29483                .filter(|unit| unit.identifier() == "value_type")
29484                .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29485            "the alias must not become a field: {declarations:#?}"
29486        );
29487    }
29488
29489    #[test]
29490    #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
29491    fn recovered_consecutive_export_classes_agree_with_expansion() {
29492        let source =
29493            function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
29494        let tree = crate::grammar_test_support::tree(&source);
29495        for name in ["Exception", "Invalid_Argument"] {
29496            crate::grammar_test_support::assert_function_like_export_class_head(
29497                &tree, &source, name,
29498            );
29499        }
29500        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29501        let actual = parse_cpp_declarations(&source, "expansion.h");
29502        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29503        assert_eq!(
29504            declaration_expansion_inventory(&actual),
29505            declaration_expansion_inventory(&explicit)
29506        );
29507    }
29508
29509    #[test]
29510    #[hardwon(
29511        "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
29512    )]
29513    fn standalone_field_list_agrees_with_expansion() {
29514        let source =
29515            "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
29516        let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
29517        let tree = crate::grammar_test_support::tree(source);
29518        assert!(
29519            crate::grammar_test_support::nodes(tree.root_node())
29520                .iter()
29521                .any(|node| node.kind() == "ERROR"
29522                    && node
29523                        .named_child(0)
29524                        .is_some_and(|child| child.kind() == "identifier")),
29525            "{}",
29526            tree.root_node().to_sexp()
29527        );
29528        let actual = parse_cpp_declarations(source, "expansion.h");
29529        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29530        assert_eq!(
29531            declaration_expansion_inventory(&actual),
29532            declaration_expansion_inventory(&explicit)
29533        );
29534    }
29535
29536    #[test]
29537    #[hardwon(
29538        "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
29539    )]
29540    fn recovered_constructor_parameters_are_not_fields() {
29541        let source = r#"namespace Botan {
29542class EXPORT(2,0) Key final {
29543public:
29544    Key(int value);
29545    void run();
29546};
29547class EXPORT(2,0) DL_Group final {
29548public:
29549    ATTRIBUTE("use factory") explicit DL_Group(const char* name);
29550    DL_Group(int value);
29551    int get_p() const;
29552};
29553}"#;
29554        let actual = parse_cpp_declarations(source, "constructors.h");
29555        let expanded = source
29556            .replace("EXPORT(2,0) ", "")
29557            .replace("ATTRIBUTE(\"use factory\") ", "");
29558        let explicit = parse_cpp_declarations(&expanded, "constructors.h");
29559        assert_eq!(
29560            declaration_expansion_inventory(&actual),
29561            declaration_expansion_inventory(&explicit)
29562        );
29563    }
29564}