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/// A callable's stable declaration identity plus the lexical block's rank.
487/// The declaration ID separates overloads that share a qualified name.
488#[derive(Clone)]
489struct CppLocalBlock {
490    callable: CodeUnit,
491    fq: FqName,
492    short_name: String,
493}
494
495impl CppLocalBlock {
496    fn new(callable: &CodeUnit, block: Node<'_>) -> Self {
497        let name = cpp_synthetic_name(block);
498        let owner_id = callable.declaration_id().to_string();
499        let rank = name.rank.to_string();
500        let mut fq = callable.fq().clone();
501        let mut short_name = callable.short_name().to_string();
502        for segment in ["owner", owner_id.as_str(), name.kind, rank.as_str()] {
503            fq.push(segment_interner().intern(segment, SegmentKind::Nested));
504            short_name = cpp_join_nested_short(&short_name, segment);
505        }
506        Self {
507            fq,
508            short_name,
509            callable: callable.clone(),
510        }
511    }
512}
513
514struct CppContainer<'tree> {
515    node: Node<'tree>,
516    scope: ScopeInfo,
517}
518
519struct CppNodeWork<'tree> {
520    node: Node<'tree>,
521    scope: ScopeInfo,
522}
523
524/// Cursor over one container's remaining named children, processed one at a
525/// time (rather than all at once) so a `using namespace X;` sibling can
526/// update `scope.visible_using_namespaces` for the siblings that follow it,
527/// matching real C++ using-directive semantics. Nested container work is
528/// still pushed and fully drained before the cursor resumes (stack LIFO
529/// order), preserving the original left-to-right visitation order.
530struct CppSiblingsWork<'tree> {
531    children: std::vec::IntoIter<Node<'tree>>,
532    scope: ScopeInfo,
533}
534
535enum CppWork<'tree> {
536    Container(CppContainer<'tree>),
537    Node(CppNodeWork<'tree>),
538    Siblings(CppSiblingsWork<'tree>),
539}
540
541fn class_like_name<'tree>(
542    node: Node<'tree>,
543    source: &str,
544    ancestry: &ParentIndex<'tree>,
545) -> Option<String> {
546    let best = class_like_name_from_children(node, source);
547    if let Some(parent) = ancestry.parent(node)
548        && matches!(
549            parent.kind(),
550            "declaration" | "field_declaration" | "function_definition"
551        )
552        // A class_specifier carrying its own body proves the grammar name is
553        // the real class name: a sibling declarator then declares an object
554        // (`class X {} x;`), never a displaced class name. The gate matters
555        // when the class name is itself an all-caps token (`X`, `API`) --
556        // without it the export-macro re-read below steals the object
557        // declarator's name for the class (#2283). The genuine export-macro
558        // shapes leave the class_specifier bodyless, the same invariant
559        // recover_malformed_exported_multiple_base_class already gates on.
560        && cpp_body_node(node).is_none()
561        && node
562            .child_by_field_name("name")
563            .map(|name_node| {
564                cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name_node, source)))
565            })
566            .unwrap_or(false)
567        && let Some(recovered) = exported_class_name_from_node(parent, source)
568        && best.as_deref() != Some(recovered.as_str())
569    {
570        return Some(recovered);
571    }
572    best.or_else(|| {
573        node.child_by_field_name("name")
574            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
575            .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
576    })
577}
578
579fn class_like_name_from_children(node: Node<'_>, source: &str) -> Option<String> {
580    let mut grammar_name = None;
581    if let Some(name_node) = node.child_by_field_name("name") {
582        let name = normalize_cpp_whitespace(node_text(name_node, source));
583        if name.is_empty() {
584            return None;
585        }
586        if cpp_body_node(node).is_some() || !cpp_export_macro_token(&name) {
587            return Some(name);
588        }
589        grammar_name = Some(name);
590    }
591
592    let mut best = None;
593    let mut cursor = node.walk();
594    let mut stack = Vec::new();
595    for child in node.named_children(&mut cursor).collect::<Vec<_>>() {
596        if matches!(
597            child.kind(),
598            "field_declaration_list" | "base_class_clause" | "declaration_list" | "enumerator_list"
599        ) {
600            break;
601        }
602        stack.push(child);
603    }
604
605    while let Some(current) = stack.pop() {
606        // Native attributes decorate the class; their identifiers cannot name it.
607        if current.kind() == "attribute_declaration" {
608            continue;
609        }
610        if matches!(current.kind(), "type_identifier" | "identifier") {
611            let name = normalize_cpp_whitespace(node_text(current, source));
612            if !name.is_empty() && !cpp_export_macro_token(&name) {
613                best = Some(name);
614            }
615            continue;
616        }
617
618        push_named_children_reversed(current, &mut stack);
619    }
620    best.or(grammar_name)
621}
622
623pub fn cpp_export_macro_token(token: &str) -> bool {
624    token
625        .chars()
626        .all(|ch| ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_')
627}
628
629struct RecoveredExportedClass<'tree> {
630    declaration_node: Node<'tree>,
631    name: String,
632    body: Option<Node<'tree>>,
633    raw_supertypes: Option<Vec<String>>,
634    base_facts: Option<Vec<CppBaseSpecifierFact>>,
635    uses_initializer_body: bool,
636    /// Present only for the fragmented multiple-base export shape (issue #938).
637    /// Carries the true class-body byte region -- the members tree-sitter scattered
638    /// out of the recovered node -- so they can be reparsed and re-owned as members
639    /// rather than lost inside the truncated `initializer_list` stand-in.
640    fragmented_body: Option<FragmentedExportBody>,
641}
642
643struct RecoveredFunctionLikeExportClassPair {
644    name: String,
645    range: Range,
646    raw_supertypes: Option<Vec<String>>,
647    base_facts: Option<Vec<CppBaseSpecifierFact>>,
648    fragmented_body: FragmentedExportBody,
649}
650
651struct RecoveredEmbeddedFunctionLikeExportClass {
652    name: String,
653    range: Range,
654    raw_supertypes: Vec<String>,
655    base_facts: Vec<CppBaseSpecifierFact>,
656    fragmented_body: FragmentedExportBody,
657}
658
659struct RecoveredFunctionLikeExportClass {
660    name: String,
661    range: Range,
662    raw_supertypes: Option<Vec<String>>,
663    base_facts: Option<Vec<CppBaseSpecifierFact>>,
664    member_outcome: Option<FragmentedExportMembers>,
665}
666
667/// The recovered class-body geometry for a fragmented multiple-base export class.
668/// `[reparse_start, reparse_end)` is the interior between the class braces, kept
669/// verbatim for a region reparse (issue #941 machinery) so every recovered member
670/// keeps its exact original byte/line position. `class_range` is the full class
671/// navigation range spanning to the displaced closing brace.
672struct FragmentedExportBody {
673    reparse_start: usize,
674    reparse_end: usize,
675    class_range: Range,
676}
677
678fn recovered_fragmented_export_body(
679    body: Node<'_>,
680    class_range: Range,
681) -> Option<FragmentedExportBody> {
682    let open = body.child(0).filter(|child| child.kind() == "{")?;
683    let close = body
684        .child(body.child_count().saturating_sub(1))
685        .filter(|child| child.kind() == "}" && !child.is_missing());
686    Some(FragmentedExportBody {
687        reparse_start: open.end_byte(),
688        // A zero-width missing `}` contributes no source byte. Keep the whole
689        // body range in that case; subtracting one byte would discard the last
690        // member's semicolon and make the otherwise valid region unsafe to
691        // index. A real close token is excluded by its structured start.
692        reparse_end: close.map_or(body.end_byte(), |close| close.start_byte()),
693        class_range,
694    })
695}
696
697struct DisplacedFragmentNamespaceBoundary<'tree> {
698    class_close: Node<'tree>,
699    class_semicolon: Node<'tree>,
700    namespace_items: Vec<Node<'tree>>,
701}
702
703/// Result of validating a reparsed fragmented class body.  A complete tree can
704/// safely consume the whole region.  A partial tree may contain only the exact
705/// class-named constructor that tree-sitter merged into an access label; its
706/// remaining siblings must stay on the ordinary outer walk.
707enum FragmentedExportMembers {
708    Complete(Tree),
709    ConditionalConstructor(Tree),
710}
711
712#[derive(Clone, Copy)]
713struct DisplacedMacroClassTail {
714    split_index: usize,
715    class_range: Range,
716}
717
718fn recover_exported_class_declaration<'tree>(
719    node: Node<'tree>,
720    source: &str,
721) -> Option<RecoveredExportedClass<'tree>> {
722    if let Some(recovered) = recover_malformed_exported_base_class(node, source) {
723        return Some(recovered);
724    }
725
726    let class_node = first_class_like_child(node)?;
727    if let Some(name_node) = class_node.child_by_field_name("name") {
728        let class_name = normalize_cpp_whitespace(node_text(name_node, source));
729        if cpp_export_macro_token(&class_name) {
730            // Tree-sitter can parse `class EXPORT Name` as an EXPORT class plus a
731            // Name declarator. Only a bare declarator can be the displaced class name;
732            // wrappers describe an object whose type merely happens to look macro-like.
733            let mut cursor = node.walk();
734            if node
735                .children_by_field_name("declarator", &mut cursor)
736                .any(|declarator| !matches!(declarator.kind(), "identifier" | "type_identifier"))
737            {
738                return None;
739            }
740        } else if has_direct_cpp_declarator(node) {
741            return None;
742        }
743    }
744    let name = exported_class_name_from_node(class_node, source)?;
745    Some(RecoveredExportedClass {
746        declaration_node: class_node,
747        name,
748        body: cpp_body_node(class_node),
749        raw_supertypes: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
750            .then(|| extract_cpp_supertypes(class_node, source)),
751        base_facts: matches!(class_node.kind(), "class_specifier" | "struct_specifier")
752            .then(|| extract_cpp_base_facts(class_node, source)),
753        uses_initializer_body: false,
754        fragmented_body: None,
755    })
756}
757
758fn recover_malformed_exported_base_class<'tree>(
759    node: Node<'tree>,
760    source: &str,
761) -> Option<RecoveredExportedClass<'tree>> {
762    if node.kind() != "declaration" {
763        return None;
764    }
765    let class_node = node.child_by_field_name("type")?;
766    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
767        return None;
768    }
769    let macro_name = class_node
770        .child_by_field_name("name")
771        .and_then(|name| direct_identifier_name(name, source))?;
772    if !cpp_export_macro_token(&macro_name) {
773        return None;
774    }
775
776    let mut named_cursor = node.walk();
777    let mut named = node.named_children(&mut named_cursor);
778    // Object-like macro lines before the head are provenance modifiers under
779    // the macro-declaration grammar; the class node follows them.
780    let head_matches_class = loop {
781        let child = named.next()?;
782        if child.kind() == "macro_modifier" {
783            continue;
784        }
785        break same_node(child, class_node);
786    };
787    if !head_matches_class {
788        return None;
789    }
790    let displaced = named.find(|child| child.kind() != "attribute_declaration")?;
791    if displaced.kind() == "init_declarator" {
792        // With a single base and an empty body the entire malformed head is
793        // the initializer's declarator, rather than a sibling of it.
794        if named.next().is_some() {
795            return None;
796        }
797        let declarator = displaced.child_by_field_name("declarator")?;
798        let (name, base) = recovered_function_like_export_class_owner_node(declarator, source)?;
799        let body = displaced.child_by_field_name("value")?;
800        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
801            return None;
802        }
803        return Some(RecoveredExportedClass {
804            declaration_node: node,
805            name,
806            body: Some(body),
807            raw_supertypes: Some(vec![recovered_malformed_base_name(base, source)?]),
808            base_facts: Some(vec![recovered_base_specifier(
809                base,
810                source,
811                recovered_base_is_virtual(declarator, base, source),
812            )?]),
813            uses_initializer_body: true,
814            fragmented_body: fragmented_export_body_region(node, body, source),
815        });
816    }
817    if displaced.kind() == "parenthesized_declarator" {
818        // The macro-declaration grammar keeps the invocation, the displaced
819        // class name and the first base inside a `parenthesized_declarator`,
820        // and the remaining bases beside the body in the trailing
821        // `init_declarator`.
822        let (name, first_base_node) =
823            recovered_function_like_export_class_owner_node(displaced, source)?;
824        let remaining = named.collect::<Vec<_>>();
825        let [init] = remaining.as_slice() else {
826            return None;
827        };
828        if init.kind() != "init_declarator" {
829            return None;
830        }
831        let mut final_base_node = init.child_by_field_name("declarator")?;
832        while final_base_node.kind() == "macro_modifier_declarator" {
833            final_base_node = final_base_node.child_by_field_name("declarator")?;
834        }
835        let first_base = recovered_malformed_base_name(first_base_node, source)?;
836        let final_base = recovered_malformed_base_name(final_base_node, source)?;
837        let body = init.child_by_field_name("value")?;
838        if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
839            return None;
840        }
841        return Some(RecoveredExportedClass {
842            declaration_node: node,
843            name,
844            body: Some(body),
845            raw_supertypes: Some(vec![first_base, final_base]),
846            base_facts: Some(vec![
847                recovered_base_specifier(
848                    first_base_node,
849                    source,
850                    recovered_base_is_virtual(displaced, first_base_node, source),
851                )?,
852                recovered_base_specifier(
853                    final_base_node,
854                    source,
855                    recovered_base_is_virtual(*init, final_base_node, source),
856                )?,
857            ]),
858            uses_initializer_body: true,
859            fragmented_body: fragmented_export_body_region(node, body, source),
860        });
861    }
862    if displaced.kind() != "ERROR" {
863        return None;
864    }
865    let name = displaced_exported_class_name(displaced, source)?;
866
867    let remaining = named.collect::<Vec<_>>();
868    let init = *remaining.last()?;
869    if init.kind() != "init_declarator" {
870        return None;
871    }
872    let final_base = init
873        .child_by_field_name("declarator")
874        .and_then(|base| recovered_malformed_base_name(base, source))?;
875    let body = init.child_by_field_name("value")?;
876    // A complete reduction has a real closing brace here. In Chromium's Widget
877    // declaration, tree-sitter instead emits the same direct `}` slot as a
878    // zero-width missing node where the first body macro truncates the prefix.
879    if body.kind() != "initializer_list" || !has_direct_token(body, "}") {
880        return None;
881    }
882
883    if remaining[..remaining.len() - 1]
884        .iter()
885        .any(|child| match child.kind() {
886            "qualified_identifier"
887            | "scoped_type_identifier"
888            | "type_identifier"
889            | "identifier"
890            | "macro_modifier_declarator" => false,
891            "ERROR" => !is_malformed_inheritance_access(*child, source),
892            _ => true,
893        })
894    {
895        return None;
896    }
897
898    let mut raw_supertypes = Vec::new();
899    let mut base_facts = Vec::new();
900    for base in &remaining[..remaining.len() - 1] {
901        if base.kind() == "ERROR" {
902            continue;
903        }
904        raw_supertypes.push(recovered_malformed_base_name(*base, source)?);
905        base_facts.push(recovered_base_specifier(
906            *base,
907            source,
908            recovered_base_is_virtual(node, *base, source),
909        )?);
910    }
911    raw_supertypes.push(final_base);
912    let final_base_node = init.child_by_field_name("declarator")?;
913    base_facts.push(recovered_base_specifier(
914        final_base_node,
915        source,
916        recovered_base_is_virtual(init, final_base_node, source),
917    )?);
918
919    Some(RecoveredExportedClass {
920        declaration_node: node,
921        name,
922        body: Some(body),
923        raw_supertypes: Some(raw_supertypes),
924        base_facts: Some(base_facts),
925        uses_initializer_body: true,
926        fragmented_body: fragmented_export_body_region(node, body, source),
927    })
928}
929
930/// Locate the true class-body region for a fragmented multiple-base export class.
931///
932/// `node` is the outer `declaration`; `body` is the `initializer_list` tree-sitter
933/// emits in place of the real class body. Tree-sitter reduces that body in one of
934/// two shapes, both of which lose the members from the recovered node:
935///
936/// * Complete inline body (one-liner / empty class): the `initializer_list` carries
937///   a real closing brace and holds the whole body text inline. The interior between
938///   the braces reparses to the members directly.
939/// * Truncated body (the QGIS/Chromium shape): the `initializer_list` ends at the
940///   first member with a zero-width MISSING `}`; every later member -- and the real
941///   closing `}` (a lone-`}` `ERROR`) -- scatters to the declaration's following
942///   siblings. The interior runs from the opening brace to that displaced `}`.
943///
944/// Returns the interior byte range to reparse plus the full class navigation range.
945fn fragmented_export_body_region(
946    node: Node<'_>,
947    body: Node<'_>,
948    source: &str,
949) -> Option<FragmentedExportBody> {
950    let reparse_start = body.start_byte() + 1;
951    let close = direct_close_brace(body)?;
952    if close.end_byte() > close.start_byte() {
953        return Some(FragmentedExportBody {
954            reparse_start,
955            reparse_end: close.start_byte(),
956            class_range: cpp_declaration_range(node),
957        });
958    }
959    // The closing brace was displaced past the recovered node. A balanced nested
960    // class keeps its own braces, so the first lone-`}` sibling is this class's.
961    let mut sibling = node.next_named_sibling();
962    let displaced_close = loop {
963        let Some(current) = sibling else {
964            break displaced_fragment_namespace_boundary(node, body, source)?.class_close;
965        };
966        if cpp_is_stray_close_brace(current, source) {
967            break current;
968        }
969        sibling = current.next_named_sibling();
970    };
971    Some(FragmentedExportBody {
972        reparse_start,
973        reparse_end: displaced_close.start_byte(),
974        class_range: Range {
975            start_byte: node.start_byte(),
976            end_byte: displaced_close.end_byte(),
977            start_line: node.start_position().row + 1,
978            end_line: displaced_close.end_position().row + 1,
979        },
980    })
981}
982
983/// Locate the true class-body region for the export-macro class shape that
984/// tree-sitter promotes to a `function_definition`.
985///
986/// In this shape the synthetic function body closes at the first inline
987/// method, while the class's real members continue as root-level siblings until
988/// a stray `}` followed by the displaced class `;`. Reparse the complete
989/// interior so those siblings are visited with the recovered class scope.
990fn fragmented_export_function_body_region(
991    node: Node<'_>,
992    body: Node<'_>,
993    source: &str,
994    displaced_namespace: Option<&DisplacedFragmentNamespaceBoundary<'_>>,
995) -> Option<FragmentedExportBody> {
996    let reparse_start = body.start_byte().checked_add(1)?;
997    if let Some(boundary) = displaced_namespace {
998        return Some(FragmentedExportBody {
999            reparse_start,
1000            reparse_end: boundary.class_close.start_byte(),
1001            class_range: Range {
1002                start_byte: node.start_byte(),
1003                end_byte: boundary.class_semicolon.end_byte(),
1004                start_line: node.start_position().row + 1,
1005                end_line: boundary.class_semicolon.end_position().row + 1,
1006            },
1007        });
1008    }
1009    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1010    let boundary =
1011        fragmented_export_sibling_class_boundary(node, source, &ParentIndex::unindexed());
1012    let boundary_index = boundary.and_then(|boundary| {
1013        siblings
1014            .iter()
1015            .position(|candidate| same_node(*candidate, boundary))
1016    });
1017    let siblings = &siblings[..boundary_index.unwrap_or(siblings.len())];
1018    let mut sibling_index = 0;
1019    // A complete recovered class's synthetic wrapper is immediately followed
1020    // by its displaced semicolon (comments and a trailing attribute macro --
1021    // `} GTEST_ATTRIBUTE_UNUSED_;`, a bare-identifier expression statement --
1022    // may sit between the body and that semicolon). Only scan for a later
1023    // stray close when real member siblings intervene; otherwise every earlier
1024    // complete class would borrow the next malformed class's close and claim
1025    // its members. The trailing-attribute case is the gtest shape: the scan
1026    // borrowed a close ~1900 lines later and re-owned a following
1027    // `namespace testing { namespace internal {` block as class members,
1028    // doubling the package path ("testing::internal::testing::internal") and
1029    // mis-nesting DeathTest under ScopedTrace, tripping the package/short
1030    // boundary assert (#2297).
1031    while let Some(current) = siblings.get(sibling_index).copied() {
1032        if current.kind() == "comment" {
1033            sibling_index += 1;
1034            continue;
1035        }
1036        if is_trailing_attribute_macro_sibling(current) {
1037            sibling_index += 1;
1038            continue;
1039        }
1040        if cpp_is_stray_semicolon(current, source) {
1041            return None;
1042        }
1043        break;
1044    }
1045    while let Some(current) = siblings.get(sibling_index).copied() {
1046        let next = siblings.get(sibling_index + 1).copied();
1047        if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
1048            return Some(FragmentedExportBody {
1049                reparse_start,
1050                reparse_end: current.start_byte(),
1051                class_range: Range {
1052                    start_byte: node.start_byte(),
1053                    end_byte: semicolon.end_byte(),
1054                    start_line: node.start_position().row + 1,
1055                    end_line: semicolon.end_position().row + 1,
1056                },
1057            });
1058        }
1059        // When the final access label keeps the class close in its malformed
1060        // declaration body, tree-sitter nests the lone `}` ERROR below the
1061        // label instead of exposing it as a direct sibling. Search only the
1062        // scattered siblings after the synthetic wrapper. The first such
1063        // close is the class terminator because nested class bodies retain
1064        // their own balanced class_specifier nodes.
1065        if current.start_byte() >= body.end_byte()
1066            && let Some(close) = cpp_nested_stray_close_brace(current, source)
1067        {
1068            return Some(FragmentedExportBody {
1069                reparse_start,
1070                reparse_end: close.start_byte(),
1071                class_range: Range {
1072                    start_byte: node.start_byte(),
1073                    end_byte: current.end_byte(),
1074                    start_line: node.start_position().row + 1,
1075                    end_line: current.end_position().row + 1,
1076                },
1077            });
1078        }
1079        sibling_index += 1;
1080    }
1081    boundary.map(|boundary| FragmentedExportBody {
1082        reparse_start,
1083        reparse_end: boundary.start_byte(),
1084        class_range: Range {
1085            start_byte: node.start_byte(),
1086            end_byte: boundary.start_byte(),
1087            start_line: node.start_position().row + 1,
1088            end_line: boundary.start_position().row + 1,
1089        },
1090    })
1091}
1092
1093/// Find a later macro-export class that tree-sitter lifted through an enclosing
1094/// preprocessor container. A class that is still a direct sibling can be a
1095/// nested member of the current fragmented class, so only a changed parent is
1096/// a proven boundary between the two recovered class envelopes.
1097fn fragmented_export_sibling_class_boundary<'tree>(
1098    node: Node<'tree>,
1099    source: &str,
1100    ancestry: &ParentIndex<'tree>,
1101) -> Option<Node<'tree>> {
1102    let node_parent = ancestry.parent(node)?;
1103    cpp_following_named_siblings(node, source, ancestry)
1104        .into_iter()
1105        .find(|candidate| {
1106            recover_exported_class_function_definition(*candidate, source).is_some()
1107                && ancestry
1108                    .parent(*candidate)
1109                    .is_none_or(|candidate_parent| !same_node(node_parent, candidate_parent))
1110        })
1111}
1112
1113/// A trailing attribute macro after a recovered class's closing brace, spelled
1114/// as a bare-identifier expression statement (`GTEST_ATTRIBUTE_UNUSED_`). A
1115/// bare identifier is never a class member (members need a type), so this
1116/// sibling can only be the class's own tail (#2297).
1117fn is_trailing_attribute_macro_sibling(node: Node<'_>) -> bool {
1118    if node.kind() != "expression_statement" {
1119        return false;
1120    }
1121    let mut cursor = node.walk();
1122    let mut children = node.named_children(&mut cursor);
1123    children
1124        .next()
1125        .is_some_and(|child| child.kind() == "identifier")
1126        && children.next().is_none()
1127}
1128
1129/// Find a lone closing-brace ERROR below a scattered sibling.  A malformed
1130/// export-class wrapper can place the class close inside an access-label node,
1131/// so direct-sibling checks alone miss the boundary.  Walk named CST children
1132/// only; the helper does not inspect source text beyond the existing structured
1133/// stray-brace predicate.
1134fn cpp_nested_stray_close_brace<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1135    let mut stack = vec![node];
1136    while let Some(current) = stack.pop() {
1137        if cpp_is_stray_close_brace(current, source) {
1138            return Some(current);
1139        }
1140        let mut cursor = current.walk();
1141        stack.extend(current.named_children(&mut cursor));
1142    }
1143    None
1144}
1145
1146/// Return named siblings that follow `node`, including siblings that tree-sitter
1147/// attached to an enclosing container after malformed recovery split the local
1148/// declaration list. Stop at the first structurally visible class close so a
1149/// later namespace or exported class cannot supply the recovery boundary.
1150///
1151/// The climb takes a [`ParentIndex`] because it runs once per visited node and
1152/// walks every level to the root: with `Node::parent`, which re-descends from
1153/// the root on each call, a translation unit holding thousands of siblings --
1154/// generated instruction tables, for instance -- made this the whole cost of
1155/// indexing the file. Callers inside the declaration walk pass the walk's
1156/// index; the four that ask from outside a traversal pass
1157/// [`ParentIndex::unindexed`], which is `Node::parent` itself, so their
1158/// behaviour is unchanged.
1159fn cpp_following_named_siblings<'tree>(
1160    node: Node<'tree>,
1161    source: &str,
1162    ancestry: &ParentIndex<'tree>,
1163) -> Vec<Node<'tree>> {
1164    let mut siblings = Vec::new();
1165    let mut anchor = node;
1166    while let Some(parent) = ancestry.parent(anchor) {
1167        let at_translation_unit = parent.kind() == "translation_unit";
1168        let mut cursor = parent.walk();
1169        assert!(
1170            cursor.goto_first_child(),
1171            "ParentIndex returned a parent without children"
1172        );
1173        while !same_node(cursor.node(), anchor) {
1174            assert!(
1175                cursor.goto_next_sibling(),
1176                "ParentIndex parent does not contain its child anchor"
1177            );
1178        }
1179        while cursor.goto_next_sibling() {
1180            let current = cursor.node();
1181            if !current.is_named() {
1182                continue;
1183            }
1184            if at_translation_unit
1185                && (current.kind() == "namespace_definition"
1186                    || (current.kind() == "function_definition"
1187                        && first_class_like_child(current).is_some()))
1188            {
1189                return siblings;
1190            }
1191            siblings.push(current);
1192            if cpp_is_stray_close_brace(current, source) {
1193                if let Some(semicolon) = current
1194                    .next_named_sibling()
1195                    .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
1196                {
1197                    siblings.push(semicolon);
1198                }
1199                return siblings;
1200            }
1201            if current.start_byte() >= node.end_byte()
1202                && matches!(current.kind(), "ERROR" | "labeled_statement")
1203                && cpp_nested_stray_close_brace(current, source).is_some()
1204            {
1205                return siblings;
1206            }
1207        }
1208        anchor = parent;
1209    }
1210    siblings
1211}
1212
1213fn cpp_fragment_sibling_is_class_member(node: Node<'_>, class_end: usize, source: &str) -> bool {
1214    if node.start_byte() >= class_end {
1215        return false;
1216    }
1217    node.end_byte() <= class_end
1218        || cpp_nested_stray_close_brace(node, source)
1219            .is_some_and(|close| close.start_byte() == class_end)
1220}
1221
1222/// A class whose head tree-sitter kept in one malformed container while the
1223/// members after the first and the closing `};` scattered across that
1224/// container's following siblings.
1225struct FragmentedClassRecovery<'tree> {
1226    declaration_node: Node<'tree>,
1227    name: String,
1228    raw_supertypes: Vec<String>,
1229    body: FragmentedExportBody,
1230}
1231
1232/// Recover a class whose opening prefix is retained in one ERROR node while one
1233/// or more nested class closes and the outer close are displaced to sibling
1234/// `}`/`;` nodes. All boundaries come from tree-sitter nodes: the direct class
1235/// tokens establish nesting depth and the displaced close nodes terminate it.
1236///
1237/// Two head spellings reach the ERROR arm. A plain `class Name ... {` keeps the
1238/// `class` keyword as the container's first token. A function-like export macro
1239/// (`class BOTAN_PUBLIC_API(2, 0) Name : public virtual Base {`) is reduced to a
1240/// body-less `class_specifier` named after the macro followed by the
1241/// invocation's `(`, arguments and `)`, with the real class name and the base
1242/// clause left as bare tokens after it (#2924).
1243fn fragmented_class_body<'tree>(
1244    node: Node<'tree>,
1245    source: &str,
1246) -> Option<FragmentedClassRecovery<'tree>> {
1247    if let Some(recovered) = fragmented_plain_class_declaration_body(node, source) {
1248        return Some(recovered);
1249    }
1250    let supported_container = node.kind() == "ERROR"
1251        || matches!(node.kind(), "function_definition" | "labeled_statement") && node.has_error();
1252    if !supported_container {
1253        return None;
1254    }
1255    let mut cursor = node.walk();
1256    let children = node.children(&mut cursor).collect::<Vec<_>>();
1257    if let Some(recovered) = fragmented_export_macro_class_body(node, &children, source) {
1258        return Some(recovered);
1259    }
1260    let keyword = children.first()?;
1261    if !matches!(keyword.kind(), "class" | "struct" | "union") {
1262        return None;
1263    }
1264    let name_node = children
1265        .iter()
1266        .copied()
1267        .skip(1)
1268        .find(|child| child.is_named())?;
1269    if !matches!(name_node.kind(), "type_identifier" | "identifier") {
1270        return None;
1271    }
1272    let name = normalize_cpp_whitespace(node_text(name_node, source));
1273    if name.is_empty() || cpp_export_macro_token(&name) {
1274        return None;
1275    }
1276    let open_index = children.iter().position(|child| child.kind() == "{")?;
1277    let open = children[open_index];
1278    let body = displaced_fragment_namespace_geometry(node, source)
1279        .map(|boundary| FragmentedExportBody {
1280            reparse_start: open.end_byte(),
1281            reparse_end: boundary.class_close.start_byte(),
1282            class_range: Range {
1283                start_byte: node.start_byte(),
1284                end_byte: boundary.class_semicolon.end_byte(),
1285                start_line: node.start_position().row + 1,
1286                end_line: boundary.class_semicolon.end_position().row + 1,
1287            },
1288        })
1289        .or_else(|| fragmented_displaced_class_body(node, &children, open_index, source))?;
1290    Some(FragmentedClassRecovery {
1291        declaration_node: node,
1292        name,
1293        raw_supertypes: extract_cpp_supertypes(node, source),
1294        body,
1295    })
1296}
1297
1298/// The fragmented body of a function-like export-macro class head that
1299/// tree-sitter kept in one declaration-scope `ERROR` together with the body's
1300/// opening `{` (Botan's `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey :
1301/// public virtual EC_PublicKey {`, #2924).
1302///
1303/// The class name is read positionally by [`recovered_export_head_name`] -- the
1304/// last identifier before the head ends at `final`, at the base clause `:`, or
1305/// at the body `{` -- so no macro spelling is interpreted, and a base clause
1306/// spelled with `virtual`, with `final`, or with qualified base names needs no
1307/// arm of its own.
1308fn fragmented_export_macro_class_body<'tree>(
1309    node: Node<'tree>,
1310    children: &[Node<'tree>],
1311    source: &str,
1312) -> Option<FragmentedClassRecovery<'tree>> {
1313    let class_node = *children.first()?;
1314    if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
1315        return None;
1316    }
1317    // The identifier tree-sitter took for the class name is the export macro, a
1318    // function-like invocation when the `(` of its argument list follows it
1319    // directly. Spelling plays no part (#2557).
1320    class_node
1321        .child_by_field_name("name")
1322        .and_then(|name| direct_identifier_name(name, source))?;
1323    let invocation = *children.get(1)?;
1324    if invocation.is_named() || invocation.kind() != "(" {
1325        return None;
1326    }
1327    let open_index = children
1328        .iter()
1329        .position(|child| !child.is_named() && child.kind() == "{")?;
1330    let open = children[open_index];
1331    let name_node = recovered_export_head_name(node, open, source)?;
1332    let name = normalize_cpp_whitespace(node_text(name_node, source));
1333    if name.is_empty() {
1334        return None;
1335    }
1336    Some(FragmentedClassRecovery {
1337        declaration_node: node,
1338        name,
1339        raw_supertypes: recovered_export_head_bases(
1340            node,
1341            name_node.end_byte(),
1342            open.start_byte(),
1343            source,
1344        ),
1345        body: fragmented_displaced_class_body(node, children, open_index, source)
1346            .or_else(|| fragmented_container_close_class_body(node, open, source))?,
1347    })
1348}
1349
1350/// The class body a fragmented head opened at `children[open_index]` but could
1351/// not close: tree-sitter scattered the remaining members and the closing `};`
1352/// across the container's following siblings. The direct class tokens after the
1353/// opening brace say how many closes the nested classes consume before this
1354/// class's own, and the displaced `}`/`;` sibling pair terminates it.
1355fn fragmented_displaced_class_body(
1356    node: Node<'_>,
1357    children: &[Node<'_>],
1358    open_index: usize,
1359    source: &str,
1360) -> Option<FragmentedExportBody> {
1361    let open = children[open_index];
1362    let nested_class_opens = children[open_index + 1..]
1363        .iter()
1364        .filter(|child| matches!(child.kind(), "class" | "struct" | "union"))
1365        .count();
1366    let mut closes_remaining = 1 + nested_class_opens;
1367    let mut sibling = node.next_named_sibling();
1368    while let Some(candidate) = sibling {
1369        let next = candidate.next_named_sibling();
1370        // The class's own close: a lone `}` `ERROR` the mis-parse split off,
1371        // or -- under the macro-declaration grammar, which keeps the fragmented
1372        // head in an `ERROR` and the close as a plain child of the enclosing
1373        // list -- a bare `}` there. The `;` after it is what proves the close
1374        // is the class's own rather than the enclosing scope's.
1375        let is_close = cpp_is_stray_close_brace(candidate, source)
1376            || (candidate.kind() == "}" && !candidate.is_named());
1377        if is_close {
1378            closes_remaining -= 1;
1379            if closes_remaining == 0 {
1380                let semicolon = next.filter(|next| cpp_is_stray_semicolon(*next, source))?;
1381                if open.end_byte() >= candidate.start_byte() {
1382                    return None;
1383                }
1384                return Some(FragmentedExportBody {
1385                    reparse_start: open.end_byte(),
1386                    reparse_end: candidate.start_byte(),
1387                    class_range: Range {
1388                        start_byte: node.start_byte(),
1389                        end_byte: semicolon.end_byte(),
1390                        start_line: node.start_position().row + 1,
1391                        end_line: semicolon.end_position().row + 1,
1392                    },
1393                });
1394            }
1395        }
1396        sibling = next;
1397    }
1398    None
1399}
1400
1401/// The body of a fragmented export-macro class head whose close tree-sitter
1402/// spent on the enclosing container instead: Botan's
1403/// `class BOTAN_PUBLIC_API(2, 0) GOST_3410_PublicKey : public virtual
1404/// EC_PublicKey {` leaves every member as a `declaration_list` sibling and ends
1405/// `namespace Botan` on the class's own brace, so nothing is displaced to a
1406/// sibling `}` and [`fragmented_displaced_class_body`] finds no terminator
1407/// (#2924).
1408fn fragmented_container_close_class_body(
1409    node: Node<'_>,
1410    open: Node<'_>,
1411    source: &str,
1412) -> Option<FragmentedExportBody> {
1413    let parent = node.parent()?;
1414    if !matches!(
1415        parent.kind(),
1416        "declaration_list" | "field_declaration_list" | "compound_statement"
1417    ) {
1418        return None;
1419    }
1420    let close = direct_close_brace(parent).filter(|close| !close.is_missing())?;
1421    // The container's close is this class's close only when the body's own
1422    // brace balance says so. Without that cross-check -- the one issue #1524
1423    // already uses on a mis-closed wrapper body -- a class that really does end
1424    // earlier would claim every later container-level declaration as a member.
1425    if OrphanedNamespaceScopeIndex::build(parent, source)
1426        .matching_close_brace(open.start_byte())
1427        .map(|close| close.start_byte)
1428        != Some(close.start_byte())
1429        || open.end_byte() >= close.start_byte()
1430    {
1431        return None;
1432    }
1433    Some(FragmentedExportBody {
1434        reparse_start: open.end_byte(),
1435        reparse_end: close.start_byte(),
1436        class_range: Range {
1437            start_byte: node.start_byte(),
1438            end_byte: close.end_byte(),
1439            start_line: node.start_position().row + 1,
1440            end_line: close.end_position().row + 1,
1441        },
1442    })
1443}
1444
1445pub(crate) fn recovered_fragmented_class_has_body(
1446    node: Node<'_>,
1447    source: &str,
1448    expected_name: &str,
1449    expected_range: &Range,
1450) -> bool {
1451    fragmented_class_body(node, source).is_some_and(|recovered| {
1452        recovered.name == expected_name
1453            && recovered.body.class_range.start_byte == expected_range.start_byte
1454            && recovered.body.class_range.end_byte == expected_range.end_byte
1455    })
1456}
1457
1458/// Recover a plain class whose parser-visible body ends inside a malformed
1459/// inline member. Tree-sitter then attaches either the next real member
1460/// declarator or the unfinished `else` branch directly to the outer function
1461/// definition and leaves the class's actual `};` among later siblings. Those
1462/// structured continuations and the close/semicolon siblings establish the
1463/// complete body envelope without interpreting source text.
1464fn fragmented_plain_class_declaration_body<'tree>(
1465    node: Node<'tree>,
1466    source: &str,
1467) -> Option<FragmentedClassRecovery<'tree>> {
1468    if !matches!(node.kind(), "declaration" | "function_definition") || !node.has_error() {
1469        return None;
1470    }
1471    let class_node = node.child_by_field_name("type")?;
1472    if !matches!(
1473        class_node.kind(),
1474        "class_specifier" | "struct_specifier" | "union_specifier"
1475    ) {
1476        return None;
1477    }
1478    let name_node = class_node.child_by_field_name("name")?;
1479    let name = normalize_cpp_whitespace(node_text(name_node, source));
1480    if name.is_empty() || cpp_export_macro_token(&name) {
1481        return None;
1482    }
1483    let body = cpp_body_node(class_node)?;
1484    if body.kind() != "field_declaration_list" {
1485        return None;
1486    }
1487    let displaced_member = if let Some(declarator) = extract_function_declarator(node) {
1488        if declarator.start_byte() < class_node.end_byte() {
1489            return None;
1490        }
1491        let mut cursor = node.walk();
1492        node.named_children(&mut cursor).any(|child| {
1493            if child.kind() != "ERROR"
1494                || child.start_byte() < class_node.end_byte()
1495                || child.end_byte() > declarator.start_byte()
1496            {
1497                return false;
1498            }
1499            let mut cursor = child.walk();
1500            let components = child.named_children(&mut cursor).collect::<Vec<_>>();
1501            let Some((return_type, attributes)) = components.split_last() else {
1502                return false;
1503            };
1504            matches!(
1505                return_type.kind(),
1506                "identifier"
1507                    | "type_identifier"
1508                    | "primitive_type"
1509                    | "decltype"
1510                    | "placeholder_type_specifier"
1511            ) && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*return_type, source)))
1512                && attributes.iter().all(|attribute| {
1513                    matches!(attribute.kind(), "identifier" | "type_identifier")
1514                        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
1515                            *attribute, source,
1516                        )))
1517                })
1518        })
1519    } else {
1520        let mut cursor = node.walk();
1521        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
1522        matches!(children.as_slice(), [candidate_class, continuation, continuation_body]
1523            if same_node(*candidate_class, class_node)
1524                && continuation.kind() == "identifier"
1525                && node_text(*continuation, source) == "else"
1526                && continuation_body.kind() == "compound_statement"
1527                && continuation_body.child(0).is_some_and(|open| open.kind() == "{")
1528                && continuation_body
1529                    .child(continuation_body.child_count().saturating_sub(1))
1530                    .is_some_and(|close| close.kind() == "}" && !close.is_missing()))
1531    };
1532    if !displaced_member {
1533        return None;
1534    }
1535    let open = body
1536        .children(&mut body.walk())
1537        .find(|child| child.kind() == "{")?;
1538    let siblings = cpp_following_named_siblings(node, source, &ParentIndex::unindexed());
1539    let ordinary_boundary =
1540        siblings
1541            .iter()
1542            .copied()
1543            .enumerate()
1544            .find_map(|(close_index, close)| {
1545                cpp_stray_close_followed_by_semicolon(
1546                    close,
1547                    siblings.get(close_index + 1).copied(),
1548                    source,
1549                )
1550                .map(|semicolon| (close, semicolon))
1551            });
1552    let (close, semicolon) =
1553        if let Some(boundary) = displaced_fragment_namespace_geometry(node, source) {
1554            (boundary.class_close, boundary.class_semicolon)
1555        } else {
1556            ordinary_boundary?
1557        };
1558    if open.end_byte() >= close.start_byte() {
1559        return None;
1560    }
1561    Some(FragmentedClassRecovery {
1562        declaration_node: class_node,
1563        name,
1564        raw_supertypes: extract_cpp_supertypes(class_node, source),
1565        body: FragmentedExportBody {
1566            reparse_start: open.end_byte(),
1567            reparse_end: close.start_byte(),
1568            class_range: Range {
1569                start_byte: class_node.start_byte(),
1570                end_byte: semicolon.end_byte(),
1571                start_line: class_node.start_position().row + 1,
1572                end_line: semicolon.end_position().row + 1,
1573            },
1574        },
1575    })
1576}
1577
1578fn displaced_export_function_namespace_shape<'tree>(
1579    declaration: Node<'tree>,
1580    source: &str,
1581) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1582    let mut nested = Vec::new();
1583    for index in (0..declaration.named_child_count()).rev() {
1584        nested.push(declaration.named_child(index)?);
1585    }
1586    while let Some(current) = nested.pop() {
1587        // A recovered export class nested in this class can consume the first
1588        // parser-visible namespace close itself. In that shape the existing
1589        // later-class boundary logic already distinguishes the nested and
1590        // namespace-sibling owners; do not mistake the nested close for this
1591        // class's terminator.
1592        if recover_exported_class_function_definition(current, source).is_some() {
1593            return None;
1594        }
1595        for index in (0..current.named_child_count()).rev() {
1596            nested.push(current.named_child(index)?);
1597        }
1598    }
1599    let mut same_envelope_sibling = declaration.next_named_sibling();
1600    while let Some(current) = same_envelope_sibling {
1601        if recover_exported_class_function_definition(current, source).is_some() {
1602            return None;
1603        }
1604        same_envelope_sibling = current.next_named_sibling();
1605    }
1606    let declaration_list = declaration.parent()?;
1607    let (namespace, class_close, trailing_semicolon) =
1608        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1609    // A chain of malformed export classes can consume one parser-visible
1610    // namespace close per class. Walk through the enclosing sibling levels so
1611    // the later real namespace close remains the structural boundary; a
1612    // direct next-sibling walk stops at the first collapsed namespace and
1613    // incorrectly makes its intervening items members of this class.
1614    let siblings = cpp_following_named_siblings(namespace, source, &ParentIndex::unindexed());
1615    let trailing_index = siblings
1616        .iter()
1617        .position(|candidate| same_node(*candidate, trailing_semicolon))?;
1618    if siblings.get(trailing_index + 1).is_some_and(|candidate| {
1619        recover_exported_class_function_definition(*candidate, source).is_some()
1620    }) {
1621        // Consecutive recovered classes already have an exact sibling-class
1622        // boundary. Preserve that established path, including nested export
1623        // classes, instead of interpreting the first class close as a
1624        // collapsed namespace boundary.
1625        return None;
1626    }
1627    let mut namespace_items = Vec::new();
1628    let mut nested_fragment_end = 0;
1629    for current in siblings.into_iter().skip(trailing_index + 1) {
1630        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1631        {
1632            return Some(DisplacedFragmentNamespaceBoundary {
1633                class_close,
1634                class_semicolon: trailing_semicolon,
1635                namespace_items,
1636            });
1637        }
1638        if current.start_byte() >= nested_fragment_end
1639            && let Some(recovered) = fragmented_class_body(current, source)
1640        {
1641            nested_fragment_end = recovered.body.class_range.end_byte;
1642        } else if current.start_byte() >= nested_fragment_end
1643            && recover_exported_class_function_definition(current, source).is_some()
1644            && let Some(body) = cpp_body_node(current)
1645            && let Some(fragmented) =
1646                fragmented_export_function_body_region(current, body, source, None)
1647        {
1648            nested_fragment_end = fragmented.class_range.end_byte;
1649        }
1650        namespace_items.push(current);
1651    }
1652    None
1653}
1654
1655fn displaced_fragment_namespace_boundary<'tree>(
1656    declaration: Node<'tree>,
1657    body: Node<'tree>,
1658    source: &str,
1659) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1660    let boundary = displaced_fragment_namespace_geometry(declaration, source)?;
1661    let reparse_start = body.start_byte() + 1;
1662    let tree = cpp_reparse_region_items(source, reparse_start, boundary.class_close.start_byte())?;
1663    cpp_reparsed_members_are_indexable(tree.root_node(), source).then_some(boundary)
1664}
1665
1666/// Recover the class/namespace brace geometry for a declaration whose class
1667/// close tree-sitter consumed as the enclosing namespace close. This proof is
1668/// independent of whether every member in the class body can be reparsed: the
1669/// ordinary-tree fallback can still re-own bounded sibling declarations when
1670/// an unknown macro makes the complete body reparse unsafe.
1671fn displaced_fragment_namespace_geometry<'tree>(
1672    declaration: Node<'tree>,
1673    source: &str,
1674) -> Option<DisplacedFragmentNamespaceBoundary<'tree>> {
1675    // A templated class's malformed function wrapper remains beneath the
1676    // template node even though its later members have escaped to the
1677    // enclosing declaration list. Lift only that exact declaration child.
1678    let envelope = declaration
1679        .parent()
1680        .filter(|parent| {
1681            parent.kind() == "template_declaration"
1682                && last_named_child(*parent).is_some_and(|child| same_node(child, declaration))
1683        })
1684        .unwrap_or(declaration);
1685    let declaration_list = envelope.parent()?;
1686    let (_, class_close, trailing_semicolon) =
1687        cpp_fragment_namespace_boundary_nodes(declaration_list)?;
1688    let mut namespace_items = Vec::new();
1689    let mut sibling = trailing_semicolon.next_named_sibling();
1690    let mut nested_fragment_end = 0;
1691    loop {
1692        let current = sibling?;
1693        if current.start_byte() >= nested_fragment_end && cpp_is_stray_close_brace(current, source)
1694        {
1695            break;
1696        }
1697        if current.start_byte() >= nested_fragment_end
1698            && let Some(recovered) = fragmented_class_body(current, source)
1699        {
1700            nested_fragment_end = recovered.body.class_range.end_byte;
1701        }
1702        namespace_items.push(current);
1703        sibling = current.next_named_sibling();
1704    }
1705    Some(DisplacedFragmentNamespaceBoundary {
1706        class_close,
1707        class_semicolon: trailing_semicolon,
1708        namespace_items,
1709    })
1710}
1711
1712fn cpp_fragment_namespace_boundary_nodes<'tree>(
1713    declaration_list: Node<'tree>,
1714) -> Option<(Node<'tree>, Node<'tree>, Node<'tree>)> {
1715    if declaration_list.kind() != "declaration_list" {
1716        return None;
1717    }
1718    let namespace = declaration_list.parent()?;
1719    if namespace.kind() != "namespace_definition"
1720        || namespace.child_by_field_name("body") != Some(declaration_list)
1721    {
1722        return None;
1723    }
1724    let class_close = direct_close_brace(declaration_list)?;
1725    let trailing_semicolon = namespace.next_named_sibling()?;
1726    (trailing_semicolon.kind() == "expression_statement"
1727        && trailing_semicolon.named_child_count() == 0)
1728        .then_some((namespace, class_close, trailing_semicolon))
1729}
1730
1731/// The direct `}` child of a node, real or MISSING (a MISSING brace is zero-width).
1732fn direct_close_brace(node: Node<'_>) -> Option<Node<'_>> {
1733    (0..node.child_count())
1734        .filter_map(|index| node.child(index))
1735        .find(|child| !child.is_named() && child.kind() == "}")
1736}
1737
1738/// A displaced lone closing brace: the class close that the fragmented multiple-base
1739/// mis-parse split off past the recovered declaration as a bare `}` `ERROR`.
1740fn cpp_is_stray_close_brace(node: Node<'_>, source: &str) -> bool {
1741    node.kind() == "ERROR" && node_text(node, source).trim() == "}"
1742}
1743
1744fn displaced_exported_class_name(node: Node<'_>, source: &str) -> Option<String> {
1745    let mut name = None;
1746    let mut colon_count = 0;
1747    let mut access_count = 0;
1748    for index in 0..node.child_count() {
1749        let child = node.child(index)?;
1750        match child.kind() {
1751            "identifier" | "type_identifier" if child.is_named() => {
1752                if name.is_some() {
1753                    return None;
1754                }
1755                let candidate = normalize_cpp_whitespace(node_text(child, source));
1756                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1757                    return None;
1758                }
1759                name = Some(candidate);
1760            }
1761            "template_function" | "template_type" if child.is_named() => {
1762                if name.is_some() {
1763                    return None;
1764                }
1765                let candidate = child
1766                    .child_by_field_name("name")
1767                    .and_then(|name| direct_identifier_name(name, source))?;
1768                if candidate.is_empty() || cpp_export_macro_token(&candidate) {
1769                    return None;
1770                }
1771                name = Some(candidate);
1772            }
1773            ":" if !child.is_named() => colon_count += 1,
1774            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
1775            _ => return None,
1776        }
1777    }
1778    (colon_count == 1 && access_count == 1)
1779        .then_some(name)
1780        .flatten()
1781}
1782
1783fn is_malformed_inheritance_access(node: Node<'_>, source: &str) -> bool {
1784    if node.kind() != "ERROR" || node.named_child_count() != 1 {
1785        return false;
1786    }
1787    node.named_child(0)
1788        .and_then(|child| direct_identifier_name(child, source))
1789        .is_some_and(|name| cpp_is_inheritance_access_specifier(&name))
1790}
1791
1792use brokk_bifrost_core::analyzer::tree_walk::has_token_child as has_direct_token;
1793
1794fn recovered_malformed_base_name(node: Node<'_>, source: &str) -> Option<String> {
1795    match node.kind() {
1796        "type_identifier" | "identifier" | "namespace_identifier" | "field_identifier" => {
1797            recovered_base_atom(node, source)
1798        }
1799        // The macro-declaration grammar wraps an access keyword and the base
1800        // as a `macro_modifier_declarator`; the base is its declarator.
1801        "macro_modifier_declarator" => node
1802            .child_by_field_name("declarator")
1803            .and_then(|declarator| recovered_malformed_base_name(declarator, source)),
1804        "template_type" | "template_function" => node
1805            .child_by_field_name("name")
1806            .and_then(|name| recovered_malformed_base_name(name, source)),
1807        "ERROR" => None,
1808        "qualified_identifier" | "scoped_type_identifier" => {
1809            let suffix = node
1810                .child_by_field_name("name")
1811                .and_then(|name| recovered_malformed_base_name(name, source))?;
1812            let scope = node
1813                .child_by_field_name("scope")
1814                .and_then(|scope| recovered_malformed_base_name(scope, source))?;
1815            let prefix = if matches!(scope.as_str(), "public" | "protected" | "private") {
1816                malformed_qualified_prefix(node, source)?
1817            } else {
1818                if malformed_qualified_prefix(node, source).is_some() {
1819                    return None;
1820                }
1821                scope
1822            };
1823            Some(format!("{prefix}::{suffix}"))
1824        }
1825        _ => None,
1826    }
1827}
1828
1829fn recovered_base_components(node: Node<'_>, source: &str) -> Option<Vec<String>> {
1830    let mut pending = vec![node];
1831    let mut components = Vec::new();
1832    while let Some(current) = pending.pop() {
1833        match current.kind() {
1834            "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier" => {
1835                components.push(recovered_base_atom(current, source)?);
1836            }
1837            "macro_modifier_declarator" => {
1838                pending.push(current.child_by_field_name("declarator")?);
1839            }
1840            "template_type" | "template_function" => {
1841                pending.push(current.child_by_field_name("name")?);
1842            }
1843            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
1844                pending.push(current.child_by_field_name("name")?);
1845                if let Some(scope) = current.child_by_field_name("scope") {
1846                    // Malformed export bases can put the access keyword in
1847                    // `scope` and the real scope in the direct ERROR child.
1848                    // Keep the exact AST recovery used by the declaration.
1849                    if recovered_base_atom(scope, source).is_some_and(|name| {
1850                        matches!(name.as_str(), "public" | "protected" | "private")
1851                    }) {
1852                        pending.push(malformed_qualified_prefix_node(current, source)?);
1853                    } else {
1854                        if malformed_qualified_prefix_node(current, source).is_some() {
1855                            return None;
1856                        }
1857                        pending.push(scope);
1858                    }
1859                }
1860            }
1861            _ => return None,
1862        }
1863    }
1864    Some(components)
1865}
1866
1867fn recovered_base_specifier(
1868    node: Node<'_>,
1869    source: &str,
1870    is_virtual: bool,
1871) -> Option<CppBaseSpecifierFact> {
1872    let components = recovered_base_components(node, source)?;
1873    crate::source_facts::base_specifier_fact(
1874        node,
1875        components,
1876        is_virtual,
1877        CppAccessSpecifier::Unknown,
1878    )
1879}
1880
1881fn recovered_base_is_virtual(container: Node<'_>, base: Node<'_>, source: &str) -> bool {
1882    // Recovery can put the qualifier in an ERROR sibling or inside the base
1883    // node. Read only the AST prefix through this base, resetting at preceding
1884    // inheritance delimiters so another base's qualifier cannot leak here.
1885    let mut pending = vec![container];
1886    let mut is_virtual = false;
1887    while let Some(node) = pending.pop() {
1888        if node.start_byte() >= base.end_byte() {
1889            break;
1890        }
1891        if node.child_count() != 0 {
1892            let mut cursor = node.walk();
1893            let children = node
1894                .children(&mut cursor)
1895                .take_while(|child| child.start_byte() < base.end_byte())
1896                .collect::<Vec<_>>();
1897            pending.extend(children.into_iter().rev());
1898            continue;
1899        }
1900        if node.start_byte() < base.start_byte() && matches!(node.kind(), "," | ":") {
1901            is_virtual = false;
1902        } else if node.kind() == "virtual"
1903            || recovered_base_atom(node, source).is_some_and(|name| name == "virtual")
1904        {
1905            is_virtual = true;
1906        }
1907    }
1908    is_virtual
1909}
1910
1911fn recovered_base_atom(node: Node<'_>, source: &str) -> Option<String> {
1912    if !matches!(
1913        node.kind(),
1914        "identifier" | "type_identifier" | "namespace_identifier" | "field_identifier"
1915    ) {
1916        return None;
1917    }
1918    let name = normalize_cpp_whitespace(node_text(node, source));
1919    (!name.is_empty()).then_some(name)
1920}
1921
1922fn malformed_qualified_prefix(node: Node<'_>, source: &str) -> Option<String> {
1923    malformed_qualified_prefix_node(node, source)
1924        .and_then(|prefix| recovered_base_atom(prefix, source))
1925}
1926
1927fn malformed_qualified_prefix_node<'tree>(node: Node<'tree>, source: &str) -> Option<Node<'tree>> {
1928    let mut prefix = None;
1929    let mut cursor = node.walk();
1930    for error in node
1931        .named_children(&mut cursor)
1932        .filter(|child| child.kind() == "ERROR")
1933    {
1934        if prefix.is_some() {
1935            return None;
1936        }
1937        // `public virtual Botan::EC_PublicKey` reduces the access specifier to
1938        // the qualified name's scope and leaves `virtual` beside the real
1939        // qualifier inside the `ERROR`. `virtual` is a base specifier, never a
1940        // component of the base's name (#2924).
1941        let mut error_cursor = error.walk();
1942        let atoms = error
1943            .named_children(&mut error_cursor)
1944            .map(|child| recovered_base_atom(child, source).map(|name| (child, name)))
1945            .collect::<Option<Vec<_>>>()?;
1946        let [atom] = atoms
1947            .iter()
1948            .filter(|(_, name)| name != "virtual")
1949            .collect::<Vec<_>>()[..]
1950        else {
1951            return None;
1952        };
1953        prefix = Some(atom.0);
1954    }
1955    prefix
1956}
1957
1958type RecoveredFunctionDefinitionClass<'tree> = (
1959    Node<'tree>,
1960    String,
1961    Option<Vec<String>>,
1962    Option<Vec<CppBaseSpecifierFact>>,
1963);
1964/// One declaration an attribute-like macro invocation swallowed into a
1965/// declaration-scope `ERROR`, with the byte range that spells it.
1966/// What [`stranded_declaration_run`] read out of one node.
1967struct StrandedRun<'tree> {
1968    declarations: Vec<MacroWrappedDeclaration<'tree>>,
1969    /// Whether every part of the node read as part of a declaration. False when
1970    /// a part the reader does not understand ended it early, or when the last
1971    /// parts were types with no declarator after them. Callers that index what
1972    /// was found keep the declarations either way; a caller deciding whether a
1973    /// whole region is safe to index requires this.
1974    complete: bool,
1975}
1976
1977struct MacroWrappedDeclaration<'tree> {
1978    declarator: Node<'tree>,
1979    range: Range,
1980    /// Whether the recovered declaration spells `static`. The envelope hides
1981    /// that keyword from the ordinary linkage reader, which looks for it among
1982    /// a declaration node's own children, and internal linkage is what decides
1983    /// whether a header declaration and a body in another file are one symbol.
1984    is_static: bool,
1985}
1986
1987/// Whether `node` stands where declarations live: directly in the translation
1988/// unit, in a `namespace` or `extern "C"` body, or in an `ERROR` that itself
1989/// stands in one of those.
1990///
1991/// An `ERROR` is a grouping failure, never a scope, so what it holds stands
1992/// where it stands. The case that matters is a whole translation unit the
1993/// parser could not close: whisper.cpp's `include/whisper.h` opens
1994/// `extern "C" {` inside `#ifdef __cplusplus` and closes it inside another one,
1995/// so the braces never balance in the token stream tree-sitter sees, the file's
1996/// own root node is an `ERROR`, and every top-level declaration in the file is
1997/// a child of it (#3094). Without this the macro recoveries below read nothing
1998/// in such a file, because none of their envelopes is in a declaration scope
1999/// the parser managed to build.
2000fn is_declaration_scope_position<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2001    declaration_scope_container(node, ancestry).is_some()
2002}
2003
2004/// The container `node` declares in, for [`is_declaration_scope_position`]. Its
2005/// end is where a parse failure that starts at `node` can still be doing
2006/// damage: a declaration scope has no synchronization point of its own, so the
2007/// parser carries such a failure to the scope's close.
2008fn declaration_scope_container<'tree>(
2009    node: Node<'tree>,
2010    ancestry: &ParentIndex<'tree>,
2011) -> Option<Node<'tree>> {
2012    let mut parent = ancestry.parent(node)?;
2013    loop {
2014        match parent.kind() {
2015            "translation_unit" => return Some(parent),
2016            "declaration_list" => {
2017                return ancestry
2018                    .parent(parent)
2019                    .is_some_and(|grandparent| {
2020                        matches!(
2021                            grandparent.kind(),
2022                            "namespace_definition" | "linkage_specification"
2023                        )
2024                    })
2025                    .then_some(parent);
2026            }
2027            "ERROR" => match ancestry.parent(parent) {
2028                Some(grandparent) => parent = grandparent,
2029                // A root `ERROR` is the translation unit the parser could not
2030                // build, so its children stand at file scope.
2031                None => return Some(parent),
2032            },
2033            // A preprocessor conditional is a transparent declaration-scope
2034            // wrapper when it sits directly under the translation unit or a
2035            // namespace/linkage declaration list. Keep climbing through it;
2036            // the next ordinary construct still decides whether the enclosed
2037            // error is at declaration scope. In particular, a conditional
2038            // inside a function reaches its compound statement and is
2039            // rejected below rather than becoming a global recovery region.
2040            "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif" | "preproc_else" => {
2041                parent = ancestry.parent(parent)?;
2042            }
2043            _ => return None,
2044        }
2045    }
2046}
2047
2048/// Whether `node` is an `ERROR` the parser produced where declarations live.
2049fn is_declaration_scope_error<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2050    node.kind() == "ERROR" && is_declaration_scope_position(node, ancestry)
2051}
2052
2053/// The identifier an object-like macro marker wraps. The grammar admits a lone
2054/// identifier as a `macro_modifier` in a declaration's specifier run, or as
2055/// the prefix of a `macro_modifier_declarator`, only where the declaration
2056/// would otherwise not parse, so the modifier is always the structured reading
2057/// of a macro the workspace never expands: it is provenance about the
2058/// declaration, never a declarator of its own.
2059pub(crate) fn macro_modifier_identifier(node: Node<'_>) -> Option<Node<'_>> {
2060    if node.kind() != "macro_modifier" {
2061        return None;
2062    }
2063    node.named_child(0)
2064        .filter(|child| child.kind() == "identifier")
2065}
2066
2067/// The modifier word and inner declarator a `macro_modifier_declarator`
2068/// wraps: the grammar's structured reading of an unexpanded object-like macro
2069/// before a declarator. In a statement-context class-body reparse the modifier
2070/// can also be the member's return type, which the grammar could not separate
2071/// from the declarator and admitted as a modifier instead.
2072pub(crate) fn macro_modifier_declarator_parts(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
2073    if node.kind() != "macro_modifier_declarator" {
2074        return None;
2075    }
2076    let mut cursor = node.walk();
2077    let modifier = node
2078        .named_children(&mut cursor)
2079        .find(|child| child.kind() == "macro_modifier")
2080        .and_then(macro_modifier_identifier)?;
2081    let declarator = node.child_by_field_name("declarator")?;
2082    Some((modifier, declarator))
2083}
2084
2085/// Whether `node` can only be part of what precedes a declarator -- a type, a
2086/// specifier, or a word of the attribute macro's own text that the lexer left
2087/// as a bare identifier or, under the macro-declaration grammar, wrapped as a
2088/// `macro_modifier` -- so a run of these followed by a declarator is one
2089/// declaration the parser failed to group.
2090fn is_recovered_declaration_type_part(node: Node<'_>) -> bool {
2091    matches!(
2092        node.kind(),
2093        "identifier"
2094            | "type_identifier"
2095            | "primitive_type"
2096            | "sized_type_specifier"
2097            | "struct_specifier"
2098            | "union_specifier"
2099            | "enum_specifier"
2100            | "type_qualifier"
2101            | "storage_class_specifier"
2102            | "explicit_function_specifier"
2103            | "virtual_function_specifier"
2104            | "qualified_identifier"
2105            | "template_type"
2106            | "dependent_type"
2107            | "placeholder_type_specifier"
2108            | "macro_modifier"
2109            | "macro_type_specifier"
2110    )
2111}
2112
2113/// Whether `node` is the `ERROR` tree-sitter leaves for a macro argument that
2114/// is not a declaration -- the hint string of `DEPRECATED(decl, "hint")`, whose
2115/// words the lexer reports as bare identifiers. Anything else in such an
2116/// `ERROR` stops the recovery, so a macro argument that carries structure this
2117/// recovery does not understand is never guessed at.
2118fn is_macro_argument_error(node: Node<'_>) -> bool {
2119    if node.kind() != "ERROR" {
2120        return false;
2121    }
2122    let mut cursor = node.walk();
2123    node.named_children(&mut cursor).all(|child| {
2124        matches!(
2125            child.kind(),
2126            "identifier" | "number_literal" | "char_literal" | "string_literal" | "comment"
2127        )
2128    })
2129}
2130
2131/// The end of a recovered declaration: the declarator's end, extended across
2132/// the `;` the grammar left beside it inside the envelope.
2133///
2134/// The envelope's own end is the hard boundary. The parser hands the last
2135/// declaration's `;` to the sibling statement it recovered with, outside the
2136/// envelope, and a range that reached it would no longer lie inside one node --
2137/// which is how every reader (including the resolver's climb from a range to
2138/// the node that declares it) finds a recovered declaration again.
2139fn recovered_declaration_end(declarator: Node<'_>) -> usize {
2140    declarator
2141        .next_sibling()
2142        .filter(|sibling| sibling.kind() == ";" && !sibling.is_missing())
2143        .map_or_else(|| declarator.end_byte(), |semicolon| semicolon.end_byte())
2144}
2145
2146/// The declarations `node` holds after an attribute-like macro cost the parser
2147/// their grouping, in source order.
2148///
2149/// The parser packs the parts into whichever slots it has left. In whisper's
2150/// `DEPRECATED(LLAMA_API T * f(a), "hint");` the wrapped declaration's `type`
2151/// field takes the export macro, the real return type and the *next*
2152/// declaration's declarator both end up inside one sibling `ERROR`, and the
2153/// `declarator` field takes whatever declaration the recovery reached last. In
2154/// Botan's `BOTAN_DEPRECATED("text") explicit Ctor(T);` the string's words
2155/// arrive as bare identifiers and the attributed member and the member after it
2156/// share one declarator node.
2157///
2158/// Both are the same failure, so both get the same reading: flatten the node's
2159/// parts -- splicing each nested `ERROR`'s own children in place, since an
2160/// `ERROR` here is only a grouping failure -- and read the flat run as what it
2161/// spells, a run of type-and-specifier tokens followed by a declarator, over
2162/// and over.
2163///
2164/// Fails closed. A part that is neither a declarator nor something that can
2165/// only precede one ends the recovery there, and the declarations before it are
2166/// kept.
2167fn stranded_declaration_run<'tree>(node: Node<'tree>, source: &str) -> StrandedRun<'tree> {
2168    let mut parts = Vec::new();
2169    let mut cursor = node.walk();
2170    for child in node.named_children(&mut cursor) {
2171        if child.kind() == "ERROR" {
2172            let mut error_cursor = child.walk();
2173            parts.extend(child.named_children(&mut error_cursor));
2174        } else {
2175            parts.push(child);
2176        }
2177    }
2178
2179    let mut declarations = Vec::new();
2180    let mut start = None;
2181    let mut is_static = false;
2182    let mut complete = true;
2183    for part in parts {
2184        if part.kind() == "comment" {
2185            continue;
2186        }
2187        if let Some(declarator) = extract_function_declarator(part) {
2188            let start_byte = start.take().unwrap_or_else(|| part.start_byte());
2189            declarations.push(MacroWrappedDeclaration {
2190                declarator,
2191                range: cpp_recovery_window(source, start_byte, recovered_declaration_end(part)),
2192                is_static,
2193            });
2194            is_static = false;
2195            continue;
2196        }
2197        if !is_recovered_declaration_type_part(part) {
2198            complete = false;
2199            break;
2200        }
2201        is_static |= part.kind() == "storage_class_specifier"
2202            && normalize_cpp_whitespace(node_text(part, source)) == "static";
2203        start.get_or_insert(part.start_byte());
2204    }
2205    StrandedRun {
2206        declarations,
2207        complete: complete && start.is_none(),
2208    }
2209}
2210
2211/// The declarations an attribute-like macro invocation swallowed into a
2212/// declaration-scope `ERROR`, in source order.
2213///
2214/// whisper.cpp's bundled `llama.h` deprecates a function by wrapping the whole
2215/// declaration in a macro call:
2216///
2217/// ```text
2218/// DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
2219///                  struct llama_model * model,
2220///           struct llama_context_params   params),
2221///         "use llama_init_from_model instead");
2222/// LLAMA_API int32_t llama_tokenize(const struct llama_vocab * vocab, ...);
2223/// ```
2224///
2225/// tree-sitter cannot know `DEPRECATED` is a macro, so it emits one `ERROR`
2226/// holding the macro name, the wrapped declaration as a
2227/// `parameter_declaration`, the hint string as another `ERROR`, and then every
2228/// following declaration as a further `parameter_declaration` until it
2229/// recovers. That is what removed `llama_tokenize` from the index and left the
2230/// seven-argument call in `talk-llama.cpp` with only that file's own
2231/// three-parameter `static` overload to choose from (#2552, and the
2232/// `LLAMA_API` half of #2551).
2233///
2234/// Every part of every swallowed declaration is still a real node; only their
2235/// grouping is lost. This rebuilds the grouping and reads the nodes.
2236///
2237/// Under the macro-declaration grammar the same invocation keeps the same
2238/// `ERROR` envelope, but the parts inside it are structured further: the macro
2239/// head is a whole `function_declarator` whose parameter list holds the
2240/// arguments, an export-macro word is a `macro_modifier`, a following
2241/// declaration is a `macro_modifier_declarator` or a bare
2242/// `function_declarator` in a sibling `ERROR`, and the hint string explodes
2243/// into word nodes between the `ERROR` nodes that hold its quotes. The reading
2244/// is the same either way: the invocation's own arguments, then one
2245/// declaration at a time until something that is not a declaration stops it.
2246fn macro_wrapped_declarations<'tree>(
2247    envelope: Node<'tree>,
2248    source: &str,
2249    ancestry: &ParentIndex<'tree>,
2250) -> Vec<MacroWrappedDeclaration<'tree>> {
2251    let mut declarations = Vec::new();
2252    if !is_declaration_scope_error(envelope, ancestry) {
2253        return declarations;
2254    }
2255    let mut cursor = envelope.walk();
2256    let children = envelope.named_children(&mut cursor).collect::<Vec<_>>();
2257    let [head, rest @ ..] = children.as_slice() else {
2258        return declarations;
2259    };
2260    let arguments: Vec<Node<'tree>> = if head.kind() == "identifier" {
2261        rest.to_vec()
2262    } else if head.kind() == "function_declarator"
2263        && head
2264            .child_by_field_name("declarator")
2265            .is_some_and(|name| name.kind() == "identifier")
2266    {
2267        let Some(parameters) = head
2268            .child_by_field_name("parameters")
2269            .filter(|parameters| parameters.kind() == "parameter_list")
2270        else {
2271            return declarations;
2272        };
2273        let mut parameters_cursor = parameters.walk();
2274        parameters.named_children(&mut parameters_cursor).collect()
2275    } else {
2276        return declarations;
2277    };
2278    let mut wrapped_declaration_seen = false;
2279    // The hint string's exploded words sit between the `ERROR` nodes holding
2280    // its quotes; inside that bracket they are string content, not
2281    // declaration parts, and they must not start a declaration's range.
2282    let mut in_hint = false;
2283    // Where the specifier words gathered for the declaration whose declarator
2284    // has not arrived yet. Only the macro-declaration grammar's envelopes use
2285    // this: in them an export-macro word is a sibling of the declarator
2286    // rather than a part of its `parameter_declaration`.
2287    let mut pending_start = None;
2288    let mut is_static = false;
2289    for argument in arguments {
2290        if argument.kind() == "comment" {
2291            continue;
2292        }
2293        if argument.kind() == "ERROR"
2294            && (0..argument.child_count())
2295                .filter(|index| {
2296                    argument
2297                        .child(*index)
2298                        .is_some_and(|child| child.kind() == "\"")
2299                })
2300                .count()
2301                % 2
2302                == 1
2303        {
2304            // One of the quote `ERROR`s bracketing the hint string.
2305            in_hint = !in_hint;
2306            pending_start = None;
2307            continue;
2308        }
2309        if in_hint {
2310            continue;
2311        }
2312        match argument.kind() {
2313            "parameter_declaration" => {
2314                let recovered = stranded_declaration_run(argument, source).declarations;
2315                if recovered.is_empty() {
2316                    break;
2317                }
2318                wrapped_declaration_seen = true;
2319                declarations.extend(recovered);
2320            }
2321            // A specifier word of the declaration whose declarator the parser
2322            // handed to a later sibling.
2323            "macro_modifier" if wrapped_declaration_seen => {
2324                pending_start.get_or_insert(argument.start_byte());
2325            }
2326            "storage_class_specifier" if wrapped_declaration_seen => {
2327                pending_start.get_or_insert(argument.start_byte());
2328                is_static |= normalize_cpp_whitespace(node_text(argument, source)) == "static";
2329            }
2330            "macro_modifier_declarator" if wrapped_declaration_seen => {
2331                let Some(declarator) = extract_function_declarator(argument) else {
2332                    break;
2333                };
2334                let start = pending_start
2335                    .take()
2336                    .unwrap_or_else(|| argument.start_byte());
2337                declarations.push(MacroWrappedDeclaration {
2338                    declarator,
2339                    range: cpp_recovery_window(source, start, recovered_declaration_end(argument)),
2340                    is_static,
2341                });
2342                is_static = false;
2343            }
2344            // The hint string, and only that: an argument the recovery cannot
2345            // read as a declaration is admitted before the wrapped declaration
2346            // is found, so a macro whose first argument is not a declaration
2347            // recovers nothing.
2348            "ERROR" if wrapped_declaration_seen && is_macro_argument_error(argument) => {}
2349            // A declaration the parser shredded to a bare function declarator
2350            // and its terminator, its specifier words left as the siblings
2351            // before it.
2352            "ERROR" if wrapped_declaration_seen => {
2353                let mut error_cursor = argument.walk();
2354                let mut parts = argument.named_children(&mut error_cursor);
2355                let Some(declarator) = parts
2356                    .next()
2357                    .filter(|part| part.kind() == "function_declarator")
2358                    .filter(|_| parts.next().is_none())
2359                else {
2360                    break;
2361                };
2362                let start = pending_start
2363                    .take()
2364                    .unwrap_or_else(|| declarator.start_byte());
2365                declarations.push(MacroWrappedDeclaration {
2366                    declarator,
2367                    range: cpp_recovery_window(
2368                        source,
2369                        start,
2370                        recovered_declaration_end(declarator),
2371                    ),
2372                    is_static,
2373                });
2374                is_static = false;
2375            }
2376            _ => break,
2377        }
2378    }
2379    declarations
2380}
2381
2382/// What one macro invocation swallowed when it collapsed a whole run of
2383/// declarations.
2384struct CollapsedMacroDeclarationRun {
2385    /// The byte just past the `;` that closes the invocation, which is where
2386    /// the declarations it swallowed begin.
2387    invocation_end: usize,
2388    /// The byte this recovery owns to: the close of the enclosing declaration
2389    /// scope when the invocation swallowed the declarations written after it,
2390    /// and the invocation's own end when the parser instead spread the
2391    /// invocation itself across the container's children and swallowed nothing.
2392    region_end: usize,
2393}
2394
2395/// The tokens one collapsed macro invocation spans, in source order: the leaves
2396/// of `node`, then the leaves of the siblings written after it. Comments and
2397/// the parser's own MISSING tokens are skipped; neither is in the source the
2398/// invocation is written in.
2399///
2400/// The parser puts the parts of a failed invocation wherever it has room. It
2401/// keeps them in one `ERROR`; it splits them across an `ERROR` and the
2402/// `expression_statement` it recovered with; it spreads them flat over the
2403/// container's children; it packs the tail of the file into one of the
2404/// arguments. The grouping therefore says nothing, but the token order still
2405/// spells the invocation, which is what this reads.
2406struct MacroInvocationTokens<'tree> {
2407    stack: Vec<Node<'tree>>,
2408    /// The last sibling handed to `stack`, and the point the next one is asked
2409    /// for. Held as the node rather than as its already-computed successor so
2410    /// the successor is asked for only when the walk actually runs off the end
2411    /// of what it has: `Node::next_sibling` recovers the parent first, which
2412    /// re-descends from the root, so it costs the node's position in the tree.
2413    /// Nearly every node this iterator is built for is rejected on its first
2414    /// token, before any sibling is needed, and computing one eagerly made that
2415    /// rejection cost a walk of the whole container (capstone's generated
2416    /// instruction tables, t19).
2417    frontier: Option<Node<'tree>>,
2418}
2419
2420impl<'tree> MacroInvocationTokens<'tree> {
2421    fn new(node: Node<'tree>) -> Self {
2422        Self {
2423            stack: vec![node],
2424            frontier: Some(node),
2425        }
2426    }
2427}
2428
2429impl<'tree> Iterator for MacroInvocationTokens<'tree> {
2430    type Item = Node<'tree>;
2431
2432    fn next(&mut self) -> Option<Node<'tree>> {
2433        loop {
2434            let Some(node) = self.stack.pop() else {
2435                let sibling = self.frontier?.next_sibling()?;
2436                self.frontier = Some(sibling);
2437                self.stack.push(sibling);
2438                continue;
2439            };
2440            if node.child_count() == 0 {
2441                if node.kind() == "comment" || node.is_missing() {
2442                    continue;
2443                }
2444                return Some(node);
2445            }
2446            let mut cursor = node.walk();
2447            let children = node.children(&mut cursor).collect::<Vec<_>>();
2448            self.stack.extend(children.into_iter().rev());
2449        }
2450    }
2451}
2452
2453/// The macro invocation that begins at `node` when the parser could not group
2454/// it, or `None` when `node` does not begin one.
2455///
2456/// whisper.cpp's bundled `llama.h` writes
2457///
2458/// ```text
2459/// DEPRECATED(LLAMA_API struct llama_model * llama_load_model_from_file(
2460///                          const char * path_model,
2461///           struct llama_model_params   params),
2462///         "use llama_model_load_from_file instead");
2463/// ```
2464///
2465/// and its own `include/whisper.h` writes
2466///
2467/// ```text
2468/// WHISPER_DEPRECATED(
2469///     WHISPER_API struct whisper_context * whisper_init_from_file(const char * path_model),
2470///     "use whisper_init_from_file_with_params instead"
2471/// );
2472/// ```
2473///
2474/// tree-sitter cannot know either name is a macro, so it reads the name and its
2475/// `(` as a function declarator whose close it never finds, and carries that
2476/// failure forward over the declarations written after it. In `llama.h` that
2477/// puts 57 KB, from line 481 to line 1535, into one `function_definition`
2478/// (#2551). In `whisper.h` it instead flattens the invocations over the
2479/// container's children -- a bare `identifier`, `(`, `parameter_declaration`,
2480/// `,` and an `ERROR` holding the hint, over and over -- and then packs lines
2481/// 232 to 456 into one `parameter_declaration` (#3094). The envelope is a
2482/// `function_definition` when a brace block falls in a swallowed tail, because
2483/// the parser borrows it for the body the bogus definition needs, and an
2484/// `ERROR` or a bare token when none does. None of that says anything about the
2485/// construct, so the node's kind is not the criterion; the token order is.
2486///
2487/// The invocation ends at the `)` that closes its own `(`, immediately followed
2488/// by `;`. That is unambiguous: a `)` the lexer left inside the hint text is
2489/// balanced by the `(` beside it, and a macro argument list carries no `;` of
2490/// its own, so a `;` reached before the close means this is some other
2491/// construct -- a macro-defined function body, say -- and the ordinary readers
2492/// keep the node.
2493///
2494/// Fails closed everywhere else too. The head must be an export-macro-shaped
2495/// token followed by `(`, the node must stand where declarations live, and an
2496/// invocation the ordinary [`macro_wrapped_declarations`] reader already has --
2497/// one the parser left whole in a declaration-scope `ERROR` and that swallowed
2498/// nothing past it -- is left to that reader.
2499fn collapsed_macro_declaration_run<'tree>(
2500    node: Node<'tree>,
2501    source: &str,
2502    ancestry: &ParentIndex<'tree>,
2503) -> Option<CollapsedMacroDeclarationRun> {
2504    let container_end = declaration_scope_container(node, ancestry)?.end_byte();
2505    let mut tokens = MacroInvocationTokens::new(node);
2506    let name = tokens.next()?;
2507    if !matches!(name.kind(), "identifier" | "type_identifier")
2508        || !cpp_export_macro_token(node_text(name, source))
2509    {
2510        return None;
2511    }
2512    if tokens.next()?.kind() != "(" {
2513        return None;
2514    }
2515    let mut depth = 1usize;
2516    let invocation_end = loop {
2517        let token = tokens.next()?;
2518        match token.kind() {
2519            "(" => depth += 1,
2520            ";" => return None,
2521            ")" => {
2522                depth -= 1;
2523                if depth == 0 {
2524                    let semicolon = tokens.next()?;
2525                    if semicolon.kind() != ";" {
2526                        return None;
2527                    }
2528                    break semicolon.end_byte();
2529                }
2530            }
2531            _ => {}
2532        }
2533    };
2534    // An invocation that reaches the end of its own declaration scope swallowed
2535    // nothing: there is nothing after it left to recover, and the ordinary
2536    // readers have the invocation itself. This is also what stops the scan
2537    // below from re-entering itself, since the region it reparses for one
2538    // invocation ends exactly at that invocation's `;`.
2539    if invocation_end >= container_end {
2540        return None;
2541    }
2542    // Where the damage ends. A node that reaches past its own invocation packed
2543    // real declarations into the invocation's argument list, so the parser was
2544    // still failing when it got there and everything to the close of the scope
2545    // is suspect -- in `whisper.h` the collapse takes the `{` of the enum on
2546    // line 455 with it, and every declaration from there to the end of the file
2547    // is shredded. A node that stops at or before the invocation's `;` holds
2548    // nothing but the invocation, whose own parts the parser spread over the
2549    // siblings after it.
2550    let region_end = if node.end_byte() > invocation_end {
2551        container_end
2552    } else {
2553        invocation_end
2554    };
2555    Some(CollapsedMacroDeclarationRun {
2556        invocation_end,
2557        region_end,
2558    })
2559}
2560
2561/// `MACRO("text") <member-declaration>` as tree-sitter parses it inside an
2562/// ordinary class body, and the members it swallowed.
2563///
2564/// Botan deprecates members that way:
2565///
2566/// ```text
2567/// BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
2568/// DL_Group(std::span<const uint8_t> der, DL_Group_Format format);
2569/// ```
2570///
2571/// The parser reads the macro name as the member's type and its argument list
2572/// as a parenthesized declarator, which then swallows the attributed member
2573/// *and* the member written after it: a `field_declaration` whose `type` is a
2574/// lone `type_identifier` and whose `declarator` is a `parenthesized_declarator`
2575/// opening with an `ERROR` whose first part is a bare identifier -- the first
2576/// word of the string, which the lexer could not keep together.
2577///
2578/// The macro's spelling is not the criterion; that structural shape is. The
2579/// returned declarations are in source order, the first being the attributed
2580/// member and the rest the members the declarator swallowed after it.
2581fn string_attribute_macro_member_declarators<'tree>(
2582    field: Node<'tree>,
2583    source: &str,
2584) -> Option<Vec<MacroWrappedDeclaration<'tree>>> {
2585    if field.kind() != "field_declaration"
2586        || field
2587            .child_by_field_name("type")
2588            .is_none_or(|type_node| type_node.kind() != "type_identifier")
2589    {
2590        return None;
2591    }
2592    let declarator = field.child_by_field_name("declarator")?;
2593    if declarator.kind() != "parenthesized_declarator" {
2594        return None;
2595    }
2596    let opening = declarator.named_child(0)?;
2597    if opening.kind() != "ERROR"
2598        || opening
2599            .named_child(0)
2600            .is_none_or(|word| word.kind() != "identifier")
2601    {
2602        return None;
2603    }
2604    let declarations = stranded_declaration_run(declarator, source).declarations;
2605    (!declarations.is_empty()).then_some(declarations)
2606}
2607
2608/// Whether `node` is the `MACRO("text")` invocation the region reparse of an
2609/// export-macro class body leaves in front of the members that macro decorated.
2610///
2611/// The reparse reads the class body as statements, so the attribute becomes a
2612/// call statement of its own -- with the `;` the grammar had to invent -- and
2613/// the members after it are stranded in the `ERROR` that follows.
2614fn is_string_attribute_macro_statement(node: Node<'_>) -> bool {
2615    let Some(call) = matches!(node.kind(), "expression_statement" | "ERROR")
2616        .then(|| node.named_child(0))
2617        .flatten()
2618        .filter(|child| child.kind() == "call_expression")
2619    else {
2620        return false;
2621    };
2622    call.child_by_field_name("function")
2623        .is_some_and(|function| function.kind() == "identifier")
2624        && call
2625            .child_by_field_name("arguments")
2626            .is_some_and(|arguments| {
2627                let mut cursor = arguments.walk();
2628                arguments.named_child_count() > 0
2629                    && arguments
2630                        .named_children(&mut cursor)
2631                        .all(|argument| argument.kind() == "string_literal")
2632            })
2633}
2634
2635/// The start byte of the call the region reparse left where an access-labeled
2636/// constructor was written.
2637///
2638/// A constructor is a member only inside a class body. The export-macro class
2639/// recovery reparses the body as statements, so `Ctor(params);` becomes a call
2640/// statement and `Ctor(params) : m_a(a), m_b(b) {}` collapses into one
2641/// comma-expression under `private:`, with no declarator left in the tree. It
2642/// is still spelled in the source, though, starting at the one call under the
2643/// label whose callee is the class's own name. Botan's private six-parameter
2644/// `XMSS_Parameters` constructor is the witness (#2552). More than one such
2645/// call is an ambiguity this declines, and so is a reparse from that byte that
2646/// does not yield exactly one constructor declarator there.
2647fn cpp_access_label_constructor_call_start(
2648    node: Node<'_>,
2649    class_name: &str,
2650    source: &str,
2651) -> Option<usize> {
2652    if node.kind() != "labeled_statement" {
2653        return None;
2654    }
2655    let label = node.named_child(0)?;
2656    if label.kind() != "statement_identifier"
2657        || !matches!(
2658            node_text(label, source).trim(),
2659            "public" | "private" | "protected"
2660        )
2661    {
2662        return None;
2663    }
2664    let mut starts = Vec::new();
2665    let mut stack = vec![node];
2666    while let Some(current) = stack.pop() {
2667        if current.kind() == "call_expression"
2668            && current
2669                .child_by_field_name("function")
2670                .is_some_and(|function| {
2671                    function.kind() == "identifier"
2672                        && node_text(function, source).trim() == class_name
2673                })
2674        {
2675            starts.push(current.start_byte());
2676        }
2677        // The macro-declaration grammar reads the constructor head as a
2678        // declaration whose type is the class name and whose declarators are
2679        // the shredded parameter list and initializers; the head's start is
2680        // the type's start.
2681        if current.kind() == "declaration"
2682            && current
2683                .child_by_field_name("type")
2684                .is_some_and(|type_node| {
2685                    type_node.kind() == "type_identifier"
2686                        && node_text(type_node, source).trim() == class_name
2687                })
2688        {
2689            starts.push(
2690                current
2691                    .child_by_field_name("type")
2692                    .expect("checked declaration type")
2693                    .start_byte(),
2694            );
2695        }
2696        let mut cursor = current.walk();
2697        stack.extend(current.named_children(&mut cursor));
2698    }
2699    let [start] = starts.as_slice() else {
2700        return None;
2701    };
2702    Some(*start)
2703}
2704
2705/// The `function_definition` that gives `declarator` a body, following only the
2706/// declarator chain, so a recovered callable knows whether it is a declaration
2707/// or a definition without anyone having to say.
2708fn cpp_declarator_function_definition<'tree>(
2709    declarator: Node<'tree>,
2710    ancestry: &ParentIndex<'tree>,
2711) -> Option<Node<'tree>> {
2712    let mut current = declarator;
2713    while let Some(parent) = ancestry.parent(current) {
2714        match parent.kind() {
2715            "function_definition" if parent.child_by_field_name("body").is_some() => {
2716                return Some(parent);
2717            }
2718            "pointer_declarator"
2719            | "reference_declarator"
2720            | "parenthesized_declarator"
2721            | "array_declarator" => current = parent,
2722            _ => return None,
2723        }
2724    }
2725    None
2726}
2727
2728/// Whether `node` lies in the body of a `namespace_definition` the ordinary
2729/// declaration walk still reaches, so a recovery that runs ahead of that walk
2730/// would name what it finds without the namespace.
2731fn cpp_is_inside_namespace_body<'tree>(node: Node<'tree>, ancestry: &ParentIndex<'tree>) -> bool {
2732    let mut current = node;
2733    while let Some(parent) = ancestry.parent(current) {
2734        if parent.kind() == "namespace_definition"
2735            && parent.child_by_field_name("body") == Some(current)
2736        {
2737            return true;
2738        }
2739        current = parent;
2740    }
2741    false
2742}
2743
2744/// Whether the source a recovered callable's byte range spells is a definition
2745/// (a declarator with a body) rather than a declaration.
2746///
2747/// A callable recovered from a mangled region owns no node of its own in the
2748/// file's tree, so the occurrence-role scan that climbs from the range to the
2749/// node containing it lands on whatever container the parser left and answers
2750/// for that instead -- which called Botan's inline `XMSS_Parameters` private
2751/// constructor a declaration and left its call site with nothing to navigate to
2752/// (#2552). Reparse the range on its own, the same offset-preserving reparse
2753/// extraction used, and read the answer from the one declaration it spells.
2754///
2755/// `None` when the range is not one recovered declaration on its own, which is
2756/// the case for every ordinary declaration, so callers keep their own answer.
2757pub fn recovered_callable_body_at(source: &str, range: &Range) -> Option<bool> {
2758    let tree = cpp_reparse_region_items(source, range.start_byte, range.end_byte)?;
2759    let root = tree.root_node();
2760    let mut cursor = root.walk();
2761    let items = root
2762        .named_children(&mut cursor)
2763        .filter(|child| child.kind() != "comment")
2764        .collect::<Vec<_>>();
2765    let [item] = items.as_slice() else {
2766        return None;
2767    };
2768    if item.start_byte() != range.start_byte || item.end_byte() != range.end_byte {
2769        return None;
2770    }
2771    match item.kind() {
2772        "function_definition" => Some(item.child_by_field_name("body").is_some()),
2773        "declaration" | "field_declaration" => Some(false),
2774        _ => None,
2775    }
2776}
2777
2778/// Whether `node` is an envelope an attribute-like macro invocation left where
2779/// declarations were written: the declaration-scope `ERROR`
2780/// [`macro_wrapped_declarations`] reads, or the collapsed run
2781/// [`collapsed_macro_declaration_run`] reads.
2782///
2783/// Extraction and resolution must read one definition of this shape. The
2784/// resolver climbs from a declaration's recorded byte range to the node that
2785/// declares it, and a recovered declaration's range lies inside one of these
2786/// envelopes rather than inside a `declaration` node, so the climb stops here
2787/// (the same role `is_recovered_exported_class_container` plays for a recovered
2788/// class).
2789pub fn is_macro_wrapped_declaration_envelope(node: Node<'_>, source: &str) -> bool {
2790    let ancestry = ParentIndex::unindexed();
2791    !macro_wrapped_declarations(node, source, &ancestry).is_empty()
2792        || collapsed_macro_declaration_run(node, source, &ancestry).is_some()
2793}
2794
2795fn recover_exported_class_function_definition<'tree>(
2796    node: Node<'tree>,
2797    source: &str,
2798) -> Option<RecoveredFunctionDefinitionClass<'tree>> {
2799    if node.kind() != "function_definition" {
2800        return None;
2801    }
2802    if let Some(prefix) = node.prev_named_sibling()
2803        && let Some(recovered) = recover_function_like_export_class_pair(prefix, source)
2804        && recovered.range.end_byte == node.end_byte()
2805    {
2806        return Some((
2807            node,
2808            recovered.name,
2809            recovered.raw_supertypes,
2810            recovered.base_facts,
2811        ));
2812    }
2813    let type_node = node.child_by_field_name("type")?;
2814    let declarator = node.child_by_field_name("declarator")?;
2815
2816    if matches!(
2817        type_node.kind(),
2818        "class_specifier" | "struct_specifier" | "union_specifier"
2819    ) {
2820        let type_name = type_node
2821            .child_by_field_name("name")
2822            .and_then(|name| direct_identifier_name(name, source));
2823        let exported_macro_type = type_name
2824            .as_ref()
2825            .is_some_and(|name| cpp_export_macro_token(name));
2826        if exported_macro_type {
2827            let mut cursor = node.walk();
2828            let errors_before_declarator = node
2829                .named_children(&mut cursor)
2830                .filter(|child| {
2831                    child.kind() == "ERROR"
2832                        && child.start_byte() >= type_node.end_byte()
2833                        && child.end_byte() <= declarator.start_byte()
2834                })
2835                .collect::<Vec<_>>();
2836            if let Some(name) = errors_before_declarator
2837                .iter()
2838                .find_map(|error| displaced_exported_class_name(*error, source))
2839            {
2840                let base_node = errors_before_declarator
2841                    .iter()
2842                    .any(|error| malformed_inheritance_syntax(*error))
2843                    .then_some(declarator);
2844                let raw_supertypes = base_node
2845                    .and_then(|base| recovered_malformed_base_name(base, source))
2846                    .map(|base| vec![base]);
2847                let base_facts = base_node
2848                    .and_then(|base| {
2849                        recovered_base_specifier(
2850                            base,
2851                            source,
2852                            recovered_base_is_virtual(node, base, source),
2853                        )
2854                    })
2855                    .map(|base| vec![base]);
2856                return Some((node, name, raw_supertypes, base_facts));
2857            }
2858            if errors_before_declarator
2859                .iter()
2860                .any(|error| malformed_inheritance_syntax(*error))
2861            {
2862                return None;
2863            }
2864        }
2865        if !exported_macro_type
2866            && let Some(name) = type_name
2867            && !cpp_export_macro_token(&name)
2868            && let Some(base) =
2869                recovered_postfix_export_macro_base(node, type_node, declarator, source)
2870        {
2871            let base_node =
2872                recovered_postfix_export_macro_base_node(node, type_node, declarator, source)?;
2873            let base_facts = Some(vec![recovered_base_specifier(
2874                base_node,
2875                source,
2876                recovered_base_is_virtual(node, base_node, source),
2877            )?]);
2878            return Some((node, name, Some(vec![base]), base_facts));
2879        }
2880        // The macro-declaration grammar reads the displaced class name and the
2881        // `final` that follows it as a `macro_modifier_declarator`; read the
2882        // name positionally from the head's tokens, the same rule the other
2883        // recovered export heads use (#2557).
2884        let declarator_name = direct_identifier_name(declarator, source).or_else(|| {
2885            (declarator.kind() == "macro_modifier_declarator")
2886                .then(|| node.child_by_field_name("body"))
2887                .flatten()
2888                .filter(|body| body.kind() == "compound_statement")
2889                .and_then(|body| recovered_export_head_name(node, body, source))
2890                .map(|name_node| normalize_cpp_whitespace(node_text(name_node, source)))
2891        });
2892        if let Some(name) = declarator_name
2893            && exported_macro_type
2894            && !cpp_export_macro_token(&name)
2895        {
2896            let base_node = exported_macro_type
2897                .then(|| recovered_single_base_after_declarator_node(node, declarator, source))
2898                .flatten();
2899            let raw_supertypes = base_node
2900                .and_then(|base| recovered_malformed_base_name(base, source))
2901                .map(|base| vec![base]);
2902            let base_facts = base_node
2903                .and_then(|base| {
2904                    recovered_base_specifier(
2905                        base,
2906                        source,
2907                        recovered_base_is_virtual(node, base, source),
2908                    )
2909                })
2910                .map(|base| vec![base]);
2911            return Some((node, name, raw_supertypes, base_facts));
2912        }
2913        if declarator.kind() == "parenthesized_declarator"
2914            && type_node
2915                .child_by_field_name("name")
2916                .and_then(|name| direct_identifier_name(name, source))
2917                .is_some_and(|name| cpp_export_macro_token(&name))
2918        {
2919            if let Some((name, base_node)) =
2920                recovered_function_like_export_class_owner_node(declarator, source)
2921            {
2922                return Some((
2923                    node,
2924                    name,
2925                    Some(vec![recovered_malformed_base_name(base_node, source)?]),
2926                    Some(vec![recovered_base_specifier(
2927                        base_node,
2928                        source,
2929                        recovered_base_is_virtual(node, base_node, source),
2930                    )?]),
2931                ));
2932            }
2933            let body_start = node
2934                .child_by_field_name("body")
2935                .map(|body| body.start_byte())
2936                .unwrap_or(node.end_byte());
2937            let mut cursor = node.walk();
2938            if let Some(name) = node
2939                .named_children(&mut cursor)
2940                .filter(|child| {
2941                    child.kind() == "ERROR"
2942                        && child.start_byte() >= declarator.end_byte()
2943                        && child.end_byte() <= body_start
2944                })
2945                .find_map(|error| declarator_name_from_node(error, source))
2946            {
2947                return Some((node, name, None, None));
2948            }
2949            // The macro-declaration grammar keeps a head without a base clause
2950            // whole inside the declarator: `( ERROR(2, 2)) TOTP final` with a
2951            // missing `)`, the name and `final` wrapped as a
2952            // `macro_modifier_declarator` or the name left bare. Read the name
2953            // positionally from the declarator's own tokens (#2557). The macro
2954            // is the `type` field, so a capitalized name (`TOTP`) is the class.
2955            if let Some(body) = node
2956                .child_by_field_name("body")
2957                .filter(|body| body.kind() == "compound_statement")
2958                && let Some(name_node) =
2959                    export_head_name_from_tokens(&export_head_tokens(declarator, None), source)
2960            {
2961                let name = normalize_cpp_whitespace(node_text(name_node, source));
2962                if !name.is_empty() {
2963                    let (bases, base_facts) = recovered_export_head_base_data(
2964                        declarator,
2965                        name_node.end_byte(),
2966                        body.start_byte(),
2967                        source,
2968                    );
2969                    return Some((
2970                        node,
2971                        name,
2972                        (!bases.is_empty()).then_some(bases),
2973                        (!base_facts.is_empty()).then_some(base_facts),
2974                    ));
2975                }
2976            }
2977        }
2978    }
2979
2980    // The macro-declaration grammar reads a namespace-opening macro before an
2981    // export-macro class head (`NS_ENTER(ns) class API Service { ... }`) with
2982    // the invocation as the `macro_type_specifier` type and the class head as
2983    // a `macro_modifier_declarator` chain. The class name is the last head
2984    // token before the body, per the positional rule (#2557).
2985    if type_node.kind() == "macro_type_specifier"
2986        && declarator.kind() == "macro_modifier_declarator"
2987        && type_node
2988            .child_by_field_name("name")
2989            .and_then(|name| direct_identifier_name(name, source))
2990            .is_some_and(|name| cpp_export_macro_token(&name))
2991    {
2992        let body = node
2993            .child_by_field_name("body")
2994            .filter(|body| body.kind() == "compound_statement")?;
2995        let tokens = export_head_tokens(node, Some(body));
2996        let has_class_keyword = tokens.iter().any(|token| {
2997            matches!(token.kind(), "identifier" | "type_identifier")
2998                && matches!(node_text(*token, source), "class" | "struct" | "union")
2999        });
3000        if !has_class_keyword {
3001            return None;
3002        }
3003        let name_node = recovered_export_head_name(node, body, source)?;
3004        let name = normalize_cpp_whitespace(node_text(name_node, source));
3005        if name.is_empty() || cpp_export_macro_token(&name) {
3006            return None;
3007        }
3008        return Some((node, name, None, None));
3009    }
3010
3011    let declarator_text = direct_identifier_name(declarator, source)?;
3012    if !matches!(declarator_text.as_str(), "class" | "struct" | "union") {
3013        return None;
3014    }
3015    class_identifier_before_body(node, source).map(|name| (node, name, None, None))
3016}
3017
3018fn recovered_function_like_export_class_owner_node<'tree>(
3019    declarator: Node<'tree>,
3020    source: &str,
3021) -> Option<(String, Node<'tree>)> {
3022    if declarator.kind() != "parenthesized_declarator" {
3023        return None;
3024    }
3025    let mut cursor = declarator.walk();
3026    let children = declarator.named_children(&mut cursor).collect::<Vec<_>>();
3027    let [prefix, base] = children.as_slice() else {
3028        return None;
3029    };
3030    if prefix.kind() != "ERROR"
3031        || !matches!(
3032            base.kind(),
3033            "identifier" | "type_identifier" | "qualified_identifier" | "scoped_type_identifier"
3034        )
3035    {
3036        return None;
3037    }
3038    let mut identifiers = Vec::new();
3039    let mut prefix_cursor = prefix.walk();
3040    let mut parts = prefix
3041        .named_children(&mut prefix_cursor)
3042        .collect::<Vec<_>>();
3043    // The macro-declaration grammar wraps the displaced name and its `final`
3044    // as a `macro_modifier_declarator`; read the identifiers it wraps the same
3045    // way as the bare ones the older grammar left here.
3046    while let Some(child) = parts.pop() {
3047        match child.kind() {
3048            "number_literal" | "string_literal" | "char_literal" => {}
3049            "identifier" | "type_identifier" => {
3050                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3051            }
3052            "macro_modifier" => {
3053                identifiers.push(normalize_cpp_whitespace(node_text(child, source)));
3054            }
3055            "macro_modifier_declarator" => {
3056                parts.extend(child.named_children(&mut child.walk()));
3057            }
3058            _ => return None,
3059        }
3060    }
3061    identifiers.reverse();
3062    let name = match identifiers.as_slice() {
3063        [name] => name.clone(),
3064        [name, final_token] if final_token == "final" => name.clone(),
3065        _ => return None,
3066    };
3067    if name.is_empty() || cpp_export_macro_token(&name) {
3068        return None;
3069    }
3070    Some((name, *base))
3071}
3072
3073/// Collect the base names and source facts tree-sitter scattered across the
3074/// recovered head of a function-like export-macro class. A base arrives either
3075/// as a direct sibling identifier or inside the `ERROR` node the grammar
3076/// produced for a `: public Base` fragment. The grammar leaves the `final`
3077/// specifier and access specifiers in the same position as the bases, so drop
3078/// them.
3079/// The bases a recovered function-like export-macro class head names between
3080/// `after` (the end of the class name, or of the access specifier that stands
3081/// in for it) and `before` (the start of the body, or of the `init_declarator`
3082/// that carries it). Bases arrive as bare declarator fields and inside `ERROR`
3083/// fragments, and one fragment can also hold the class name and `final`
3084/// (`M1 M2 Name final : public A`), so each part is bounded by position rather
3085/// than by the fragment that holds it.
3086fn recovered_export_head_base_data(
3087    node: Node<'_>,
3088    after: usize,
3089    before: usize,
3090    source: &str,
3091) -> (Vec<String>, Vec<CppBaseSpecifierFact>) {
3092    let within = |part: &Node<'_>| part.start_byte() >= after && part.end_byte() <= before;
3093    let mut base_nodes = Vec::new();
3094    let mut cursor = node.walk();
3095    for child in node.named_children(&mut cursor) {
3096        if child.kind() == "ERROR" {
3097            let mut error_cursor = child.walk();
3098            base_nodes.extend(
3099                child
3100                    .named_children(&mut error_cursor)
3101                    .filter(within)
3102                    .filter_map(|part| {
3103                        recovered_malformed_base_name(part, source).map(|_| (part, child))
3104                    }),
3105            );
3106        } else if child.kind() == "macro_modifier_declarator" {
3107            // The modifier words are access specifiers or decoration; the base
3108            // is the wrapper's terminal declarator.
3109            let mut current = child;
3110            while let Some(inner) = current.child_by_field_name("declarator") {
3111                if inner.kind() == "macro_modifier_declarator" {
3112                    current = inner;
3113                    continue;
3114                }
3115                if within(&inner) && recovered_malformed_base_name(inner, source).is_some() {
3116                    base_nodes.push((inner, child));
3117                }
3118                break;
3119            }
3120        } else if within(&child) && recovered_malformed_base_name(child, source).is_some() {
3121            base_nodes.push((child, node));
3122        }
3123    }
3124    let mut bases = Vec::new();
3125    let mut facts = Vec::new();
3126    for (base, container) in base_nodes {
3127        let Some(name) = recovered_malformed_base_name(base, source) else {
3128            continue;
3129        };
3130        if matches!(
3131            name.as_str(),
3132            "final" | "public" | "protected" | "private" | "virtual"
3133        ) {
3134            continue;
3135        }
3136        bases.push(name);
3137        if let Some(fact) = recovered_base_specifier(
3138            base,
3139            source,
3140            recovered_base_is_virtual(container, base, source),
3141        ) {
3142            facts.push(fact);
3143        }
3144    }
3145    (bases, facts)
3146}
3147
3148fn recovered_export_head_bases(
3149    node: Node<'_>,
3150    after: usize,
3151    before: usize,
3152    source: &str,
3153) -> Vec<String> {
3154    recovered_export_head_base_data(node, after, before, source).0
3155}
3156
3157/// Whether `token` is the `final` that closes a recovered class head. The
3158/// grammar keeps it as the `final` keyword when it recognised the head
3159/// (`M1 M2 Name final : public A`) and as an `identifier` spelled `final` when
3160/// it did not (`Name final {`, `OTHER_MACRO Name final : public A`).
3161fn recovered_export_head_final(token: Node<'_>, source: &str) -> bool {
3162    if token.is_named() {
3163        token.kind() == "identifier" && node_text(token, source) == "final"
3164    } else {
3165        token.kind() == "final"
3166    }
3167}
3168
3169/// The class name of a recovered function-like export-macro class head, read by
3170/// position rather than spelling (#2557). `node` is the sibling tree-sitter
3171/// built from the head after `class MACRO(2, 0)`, and `tail` the child that
3172/// carries the body. The head's tokens, in source order with `ERROR` fragments
3173/// flattened, read `macros... name [final] [: bases...]`: the name is the last
3174/// identifier before the head ends, and the head ends at `final`, at the base
3175/// clause `:`, or at `tail`. Every identifier before the name is decoration
3176/// (`class MACRO(2, 0) OTHER_MACRO Name`), and a class named in capitals
3177/// (`X509_CA`) is a class name like any other.
3178///
3179/// A `MISSING` identifier is a zero-width node tree-sitter invented to close a
3180/// rule, not a token of the source, so it is never the name.
3181fn recovered_export_head_name<'tree>(
3182    node: Node<'tree>,
3183    tail: Node<'tree>,
3184    source: &str,
3185) -> Option<Node<'tree>> {
3186    export_head_name_from_tokens(&export_head_tokens(node, Some(tail)), source)
3187}
3188
3189/// The class name of a head whose tokens straddle the recovered pair: the
3190/// grammar can keep `class MACRO(3, 6) Name final :` in the prefix fragment and
3191/// only the base list and the body in the sibling (Botan's TPM2 keys, #2924).
3192/// The positional rule is the same, read over the two token runs in source
3193/// order, so a name in either node wins by position rather than by which node
3194/// holds it.
3195fn recovered_export_pair_head_name<'tree>(
3196    prefix: Node<'tree>,
3197    sibling: Node<'tree>,
3198    tail: Node<'tree>,
3199    source: &str,
3200) -> Option<Node<'tree>> {
3201    let mut tokens = export_head_tokens(prefix, None);
3202    tokens.extend(export_head_tokens(sibling, Some(tail)));
3203    export_head_name_from_tokens(&tokens, source)
3204}
3205
3206/// One node's head tokens in source order, with `ERROR` fragments spliced in
3207/// place (an `ERROR` here is a grouping failure, not a construct) and stopping
3208/// before `tail` when the caller names one. A `macro_modifier` or
3209/// `macro_modifier_declarator` is the macro-declaration grammar's structured
3210/// form of the same grouping failure, so its children join the run in place
3211/// the same way, recursively.
3212fn export_head_tokens<'tree>(node: Node<'tree>, tail: Option<Node<'tree>>) -> Vec<Node<'tree>> {
3213    let mut tokens = Vec::new();
3214    let mut stack = Vec::new();
3215    let mut cursor = node.walk();
3216    stack.extend(
3217        node.children(&mut cursor)
3218            .collect::<Vec<_>>()
3219            .into_iter()
3220            .rev(),
3221    );
3222    while let Some(child) = stack.pop() {
3223        if tail.is_some_and(|tail| child.start_byte() >= tail.start_byte()) {
3224            break;
3225        }
3226        if matches!(
3227            child.kind(),
3228            "ERROR" | "macro_modifier" | "macro_modifier_declarator"
3229        ) {
3230            let mut fragment_cursor = child.walk();
3231            stack.extend(
3232                child
3233                    .children(&mut fragment_cursor)
3234                    .collect::<Vec<_>>()
3235                    .into_iter()
3236                    .rev(),
3237            );
3238        } else {
3239            tokens.push(child);
3240        }
3241    }
3242    tokens
3243}
3244
3245/// The last identifier before the head ends, per the positional rule.
3246fn export_head_name_from_tokens<'tree>(
3247    tokens: &[Node<'tree>],
3248    source: &str,
3249) -> Option<Node<'tree>> {
3250    let end = tokens
3251        .iter()
3252        .position(|token| {
3253            recovered_export_head_final(*token, source)
3254                || (!token.is_named() && token.kind() == ":")
3255        })
3256        .unwrap_or(tokens.len());
3257    export_head_last_identifier(tokens[..end].iter().copied())
3258}
3259
3260fn export_head_last_identifier<'tree>(
3261    candidates: impl IntoIterator<Item = Node<'tree>>,
3262) -> Option<Node<'tree>> {
3263    candidates
3264        .into_iter()
3265        .filter(|candidate| {
3266            candidate.is_named()
3267                && !candidate.is_missing()
3268                && matches!(
3269                    candidate.kind(),
3270                    "identifier" | "type_identifier" | "field_identifier"
3271                )
3272        })
3273        .last()
3274}
3275
3276/// The `init_declarator` whose `value` carries the class body when the grammar
3277/// keeps a function-like export-macro class head as one `declaration`.
3278fn recovered_export_init_declarator(declaration: Node<'_>) -> Option<Node<'_>> {
3279    let mut cursor = declaration.walk();
3280    declaration
3281        .named_children(&mut cursor)
3282        .find(|child| child.kind() == "init_declarator")
3283}
3284
3285/// Read the bases and the initializer-list body of a `declaration`-shaped tail
3286/// of a function-like export-macro class head. The grammar splits a base list
3287/// across bare declarator fields, `ERROR` fragments, and one trailing
3288/// `init_declarator` whose `value` is the class body. `head_end` is where the
3289/// class identity ends and the bases can begin: the end of the access specifier
3290/// when the class name went to a statement label, and the end of the class name
3291/// itself otherwise.
3292fn recovered_export_declaration_tail<'tree>(
3293    declaration: Node<'tree>,
3294    head_end: usize,
3295    source: &str,
3296) -> Option<(Vec<String>, Option<Vec<CppBaseSpecifierFact>>, Node<'tree>)> {
3297    let init = recovered_export_init_declarator(declaration)?;
3298    let body = init.child_by_field_name("value")?;
3299    if body.kind() != "initializer_list" {
3300        return None;
3301    }
3302    let (mut bases, mut facts) =
3303        recovered_export_head_base_data(declaration, head_end, init.start_byte(), source);
3304    let (more_bases, more_facts) =
3305        recovered_export_head_base_data(init, init.start_byte(), body.start_byte(), source);
3306    bases.extend(more_bases);
3307    facts.extend(more_facts);
3308    Some((bases, (!facts.is_empty()).then_some(facts), body))
3309}
3310
3311/// Whether `node` is the head fragment tree-sitter leaves where
3312/// `class MACRO(args)` was written: the class keyword and the export macro's
3313/// function-like invocation in that order, with nothing else between them.
3314/// Spelling plays no part -- the invocation's `(` following the macro
3315/// identifier directly is what makes it function-like (#2557).
3316///
3317/// Two reductions produce the head. On its own the grammar keeps a body-less
3318/// `class_specifier` named after the macro inside a declaration-scope `ERROR`,
3319/// with the invocation's `(`, arguments and `)` beside it. After object-like
3320/// macro lines -- Botan's `BOTAN_DIAGNOSTIC_PUSH` /
3321/// `BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE` -- it instead keeps a
3322/// `declaration` whose class keyword is demoted to a bare `identifier` and
3323/// whose macro invocation becomes the trailing `init_declarator` (#2924).
3324fn is_function_like_export_class_head(node: Node<'_>, source: &str) -> bool {
3325    match node.kind() {
3326        "ERROR" => {
3327            let Some(class_node) = first_class_like_child(node) else {
3328                return false;
3329            };
3330            if class_node.kind() != "class_specifier" || cpp_body_node(class_node).is_some() {
3331                return false;
3332            }
3333            if class_node
3334                .child_by_field_name("name")
3335                .and_then(|name| direct_identifier_name(name, source))
3336                .is_none()
3337            {
3338                return false;
3339            }
3340            class_node
3341                .next_sibling()
3342                .is_some_and(|invocation| !invocation.is_named() && invocation.kind() == "(")
3343        }
3344        "declaration" => {
3345            let mut cursor = node.walk();
3346            let children = node.named_children(&mut cursor).collect::<Vec<_>>();
3347            // The class keyword lost its own node: it survives as a bare
3348            // identifier spelled `class`, `struct` or `union`, which is never a
3349            // declarator or a type in well-formed code. It can sit inside an
3350            // `ERROR` beside the object-like macro names, and an `ERROR` here is
3351            // a grouping failure rather than a construct, so read through it.
3352            let Some(keyword) = children
3353                .iter()
3354                .copied()
3355                .flat_map(|child| {
3356                    let mut cursor = child.walk();
3357                    if child.kind() == "ERROR" {
3358                        child.named_children(&mut cursor).collect::<Vec<_>>()
3359                    } else {
3360                        vec![child]
3361                    }
3362                })
3363                .find(|child| {
3364                    child.kind() == "identifier"
3365                        && matches!(node_text(*child, source), "class" | "struct" | "union")
3366                })
3367            else {
3368                return false;
3369            };
3370            children.last().is_some_and(|init| {
3371                init.kind() == "init_declarator" && init.start_byte() >= keyword.end_byte() && {
3372                    let mut cursor = init.walk();
3373                    let parts = init.named_children(&mut cursor).collect::<Vec<_>>();
3374                    matches!(parts.as_slice(), [macro_name, arguments]
3375                        if matches!(macro_name.kind(), "identifier" | "type_identifier")
3376                            && arguments.kind() == "argument_list")
3377                }
3378            })
3379        }
3380        _ => false,
3381    }
3382}
3383
3384fn recover_function_like_export_class_pair(
3385    node: Node<'_>,
3386    source: &str,
3387) -> Option<RecoveredFunctionLikeExportClassPair> {
3388    if !is_function_like_export_class_head(node, source) {
3389        return None;
3390    }
3391    let sibling = node.next_named_sibling()?;
3392    let (name, raw_supertypes, base_facts, body) = match sibling.kind() {
3393        // At translation-unit scope tree-sitter can leave the malformed class
3394        // head in one ERROR node and parse its body as the adjacent compound
3395        // statement. The head still carries the export invocation, displaced
3396        // class name, and optional `final` token in source order, so recover
3397        // the name by the same positional rule as the other shapes.
3398        "compound_statement" => (
3399            recovered_export_pair_head_name(node, sibling, sibling, source)
3400                .map(|name| normalize_cpp_whitespace(node_text(name, source)))?,
3401            None,
3402            None,
3403            sibling,
3404        ),
3405        "expression_statement" => {
3406            let compound = sibling.named_child(0)?;
3407            if compound.kind() != "compound_literal_expression" {
3408                return None;
3409            }
3410            let body = compound.child_by_field_name("value")?;
3411            if body.kind() != "initializer_list" {
3412                return None;
3413            }
3414            (
3415                compound
3416                    .child_by_field_name("type")
3417                    .and_then(|name| direct_identifier_name(name, source))?,
3418                None,
3419                None,
3420                body,
3421            )
3422        }
3423        "labeled_statement" => {
3424            let label = sibling.child_by_field_name("label")?;
3425            if label.kind() != "statement_identifier" {
3426                return None;
3427            }
3428            let name = normalize_cpp_whitespace(node_text(label, source));
3429            let declaration = sibling
3430                .named_children(&mut sibling.walk())
3431                .find(|child| child.kind() == "declaration")?;
3432            let access = declaration.child_by_field_name("type")?;
3433            if !matches!(
3434                node_text(access, source),
3435                "public" | "protected" | "private"
3436            ) {
3437                return None;
3438            }
3439            let (bases, base_facts, body) =
3440                recovered_export_declaration_tail(declaration, access.end_byte(), source)?;
3441            (name, (!bases.is_empty()).then_some(bases), base_facts, body)
3442        }
3443        // `class MACRO(2, 0) Name final { ... };`,
3444        // `class MACRO(2, 0) Name final : public Base { ... };`, and
3445        // `class MACRO(2, 0) OTHER_MACRO Name { ... };`. The head's identifiers
3446        // spread over the `type` field, the declarator field, and `ERROR`
3447        // fragments beside them; the name is the last one before the head ends.
3448        "function_definition" => {
3449            let body = sibling.child_by_field_name("body")?;
3450            if body.kind() != "compound_statement" {
3451                return None;
3452            }
3453            let name_node = recovered_export_pair_head_name(node, sibling, body, source)?;
3454            let (bases, base_facts) = recovered_export_head_base_data(
3455                sibling,
3456                name_node.end_byte(),
3457                body.start_byte(),
3458                source,
3459            );
3460            (
3461                normalize_cpp_whitespace(node_text(name_node, source)),
3462                (!bases.is_empty()).then_some(bases),
3463                (!base_facts.is_empty()).then_some(base_facts),
3464                body,
3465            )
3466        }
3467        // `class MACRO(2, 0) Name final : public A, public B { ... };`. The
3468        // comma-separated base list makes the grammar keep the whole tail as one
3469        // declaration whose trailing `init_declarator` carries the body.
3470        "declaration" => {
3471            let init = recovered_export_init_declarator(sibling)?;
3472            let name_node = recovered_export_pair_head_name(node, sibling, init, source)?;
3473            let (bases, base_facts, body) =
3474                recovered_export_declaration_tail(sibling, name_node.end_byte(), source)?;
3475            (
3476                normalize_cpp_whitespace(node_text(name_node, source)),
3477                (!bases.is_empty()).then_some(bases),
3478                base_facts,
3479                body,
3480            )
3481        }
3482        _ => return None,
3483    };
3484    debug_assert!(
3485        !name.is_empty(),
3486        "a recovered class head names its class by an identifier token"
3487    );
3488    let range = Range {
3489        start_byte: node.start_byte(),
3490        end_byte: sibling.end_byte(),
3491        start_line: node.start_position().row + 1,
3492        end_line: sibling.end_position().row + 1,
3493    };
3494    Some(RecoveredFunctionLikeExportClassPair {
3495        name,
3496        raw_supertypes,
3497        base_facts,
3498        range,
3499        fragmented_body: recovered_fragmented_export_body(body, range)?,
3500    })
3501}
3502
3503/// Recover a function-like export-macro class that tree-sitter embedded in a
3504/// larger error after an earlier malformed class body. The grammar still
3505/// preserves every part of the class head: the `class` token, export macro
3506/// identifier and argument list, displaced class identifier, access specifier,
3507/// base field, and initializer-list-shaped body. Match only that complete
3508/// structured sequence and keep each recovered class's exact byte envelope.
3509fn recover_embedded_function_like_export_classes(
3510    node: Node<'_>,
3511    source: &str,
3512) -> Vec<RecoveredEmbeddedFunctionLikeExportClass> {
3513    if !node.is_error() {
3514        return Vec::new();
3515    }
3516
3517    let mut nodes = Vec::new();
3518    let mut class_tokens = Vec::new();
3519    let mut stack = vec![node];
3520    while let Some(current) = stack.pop() {
3521        nodes.push(current);
3522        // An error subtree without an anonymous class token cannot contain
3523        // a recoverable class head. Detect that before sorting or resolving
3524        // kind-id tables; the grammar spelling matches all aliased symbol ids.
3525        if !current.is_named() && current.kind() == "class" {
3526            class_tokens.push(current);
3527        }
3528        push_children_reversed(current, &mut stack);
3529    }
3530    if class_tokens.is_empty() {
3531        return Vec::new();
3532    }
3533    class_tokens.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3534    nodes.sort_unstable_by_key(|child| (child.start_byte(), child.end_byte()));
3535
3536    // The scans below ask the same handful of kind questions once per node of
3537    // the error subtree, so the kinds are resolved to symbol ids first (#3097).
3538    let language = node.language();
3539    let class_kind = NodeKindIds::new(&language, "class");
3540    let identifier_kinds = [
3541        NodeKindIds::new(&language, "identifier"),
3542        NodeKindIds::new(&language, "type_identifier"),
3543        NodeKindIds::new(&language, "field_identifier"),
3544    ];
3545    let argument_list_kind = NodeKindIds::new(&language, "argument_list");
3546    let colon_kind = NodeKindIds::new(&language, ":");
3547    let field_initializer_kind = NodeKindIds::new(&language, "field_initializer");
3548
3549    let mut recovered = Vec::new();
3550    for class_token in class_tokens {
3551        debug_assert!(class_kind.matches(class_token));
3552        let row = class_token.start_position().row;
3553        // The head after the `class` token reads `MACRO(args) macros... name
3554        // [final] : bases`. The macro is the first identifier, a function-like
3555        // invocation when its argument list is the next thing after it, and the
3556        // name is the last identifier before the head ends at `final` or at the
3557        // base clause `:`. Spelling plays no part (#2557).
3558        let is_identifier = |candidate: &Node<'_>| {
3559            !candidate.is_missing() && identifier_kinds.iter().any(|kind| kind.matches(*candidate))
3560        };
3561        let Some(macro_name) = nodes.iter().copied().find(|candidate| {
3562            candidate.start_byte() >= class_token.end_byte()
3563                && candidate.start_position().row == row
3564                && is_identifier(candidate)
3565        }) else {
3566            continue;
3567        };
3568        let Some(arguments) = nodes.iter().copied().find(|candidate| {
3569            argument_list_kind.matches(*candidate)
3570                && candidate.start_byte() >= macro_name.end_byte()
3571                && candidate.start_position().row == row
3572        }) else {
3573            continue;
3574        };
3575        if nodes.iter().any(|candidate| {
3576            is_identifier(candidate)
3577                && candidate.start_byte() >= macro_name.end_byte()
3578                && candidate.end_byte() <= arguments.start_byte()
3579        }) {
3580            continue;
3581        }
3582        let Some(head_end) = nodes.iter().copied().find(|candidate| {
3583            candidate.start_byte() >= arguments.end_byte()
3584                && (recovered_export_head_final(*candidate, source)
3585                    || (!candidate.is_named() && colon_kind.matches(*candidate)))
3586        }) else {
3587            continue;
3588        };
3589        let Some(name_node) =
3590            export_head_last_identifier(nodes.iter().copied().filter(|candidate| {
3591                is_identifier(candidate)
3592                    && candidate.start_byte() >= arguments.end_byte()
3593                    && candidate.end_byte() <= head_end.start_byte()
3594                    && candidate.start_position().row == row
3595            }))
3596        else {
3597            continue;
3598        };
3599        let name = normalize_cpp_whitespace(node_text(name_node, source));
3600        let Some(base_initializer) = nodes.iter().copied().find(|candidate| {
3601            field_initializer_kind.matches(*candidate)
3602                && candidate.start_byte() >= name_node.end_byte()
3603                && candidate
3604                    .child_by_field_name("field")
3605                    .or_else(|| candidate.named_child(0))
3606                    .is_some()
3607                && candidate
3608                    .child_by_field_name("value")
3609                    .or_else(|| {
3610                        let mut cursor = candidate.walk();
3611                        candidate
3612                            .named_children(&mut cursor)
3613                            .find(|child| child.kind() == "initializer_list")
3614                    })
3615                    .is_some_and(|value| value.kind() == "initializer_list")
3616        }) else {
3617            continue;
3618        };
3619        let has_access = nodes.iter().copied().any(|candidate| {
3620            candidate.start_byte() >= name_node.end_byte()
3621                && candidate.end_byte() <= base_initializer.start_byte()
3622                && matches!(
3623                    normalize_cpp_whitespace(node_text(candidate, source)).as_str(),
3624                    "public" | "protected" | "private"
3625                )
3626        });
3627        if !has_access {
3628            continue;
3629        }
3630        let Some(base_node) = base_initializer
3631            .child_by_field_name("field")
3632            .or_else(|| base_initializer.named_child(0))
3633        else {
3634            continue;
3635        };
3636        let Some(base) = recovered_malformed_base_name(base_node, source) else {
3637            continue;
3638        };
3639        let body = base_initializer
3640            .child_by_field_name("value")
3641            .or_else(|| {
3642                let mut cursor = base_initializer.walk();
3643                base_initializer
3644                    .named_children(&mut cursor)
3645                    .find(|child| child.kind() == "initializer_list")
3646            })
3647            .expect("initializer-list value checked above");
3648        let range = Range {
3649            start_byte: class_token.start_byte(),
3650            end_byte: body.end_byte(),
3651            start_line: class_token.start_position().row + 1,
3652            end_line: body.end_position().row + 1,
3653        };
3654        if recovered
3655            .iter()
3656            .any(|existing: &RecoveredEmbeddedFunctionLikeExportClass| {
3657                existing.name == name && existing.range == range
3658            })
3659        {
3660            continue;
3661        }
3662        let Some(base_fact) = recovered_base_specifier(base_node, source, false) else {
3663            continue;
3664        };
3665        recovered.push(RecoveredEmbeddedFunctionLikeExportClass {
3666            name,
3667            range,
3668            raw_supertypes: vec![base],
3669            base_facts: vec![base_fact],
3670            fragmented_body: match recovered_fragmented_export_body(body, range) {
3671                Some(fragmented) => fragmented,
3672                None => continue,
3673            },
3674        });
3675    }
3676    recovered
3677}
3678
3679fn lifted_function_like_export_class_namespace<'tree>(
3680    node: Node<'tree>,
3681    source: &str,
3682    ancestry: &ParentIndex<'tree>,
3683) -> Option<String> {
3684    // A long malformed body can embed the next exported class several levels
3685    // below a bogus top-level function_definition. Compare namespace evidence
3686    // against that top-level envelope, not only the recovered ERROR's direct
3687    // parent. The source tree still proves the same boundary: one earlier
3688    // malformed namespace and one later standalone closing brace.
3689    let mut anchor = node;
3690    let parent = loop {
3691        let parent = ancestry.parent(anchor)?;
3692        if parent.kind() == "translation_unit" || parent.kind().starts_with("preproc_") {
3693            break parent;
3694        }
3695        anchor = parent;
3696    };
3697    let has_later_close = parent.named_children(&mut parent.walk()).any(|sibling| {
3698        sibling.start_byte() > anchor.end_byte()
3699            && sibling.kind() == "ERROR"
3700            && sibling.named_child_count() == 0
3701            && normalize_cpp_whitespace(node_text(sibling, source)) == "}"
3702    });
3703    if !has_later_close {
3704        return None;
3705    }
3706    let candidates = parent
3707        .named_children(&mut parent.walk())
3708        .filter(|sibling| {
3709            sibling.kind() == "namespace_definition"
3710                && sibling.has_error()
3711                && sibling.end_byte() < anchor.start_byte()
3712        })
3713        .filter_map(|namespace| {
3714            namespace
3715                .child_by_field_name("name")
3716                .map(|name| normalize_cpp_whitespace(node_text(name, source)))
3717                .filter(|name| !name.is_empty() && !cpp_export_macro_token(name))
3718        })
3719        .collect::<Vec<_>>();
3720    let [namespace] = candidates.as_slice() else {
3721        return None;
3722    };
3723    Some(namespace.clone())
3724}
3725
3726pub(crate) fn recovered_function_like_export_class_pair_has_body(
3727    node: Node<'_>,
3728    source: &str,
3729    identifier: &str,
3730    range: &Range,
3731) -> bool {
3732    recover_function_like_export_class_pair(node, source).is_some_and(|recovered| {
3733        recovered.name == identifier
3734            && recovered.range.start_byte == range.start_byte
3735            && recovered.range.end_byte == range.end_byte
3736    })
3737}
3738
3739fn recovered_exported_class_has_body(
3740    node: Node<'_>,
3741    source: &str,
3742    expected_name: &str,
3743) -> Option<bool> {
3744    match node.kind() {
3745        "function_definition" => {
3746            let (class_node, name, _, _) =
3747                recover_exported_class_function_definition(node, source)?;
3748            (name == expected_name).then(|| cpp_body_node(class_node).is_some())
3749        }
3750        "declaration" | "field_declaration" => {
3751            let recovered = recover_exported_class_declaration(node, source)?;
3752            (recovered.name == expected_name).then(|| recovered.body.is_some())
3753        }
3754        _ => None,
3755    }
3756}
3757
3758/// One file's embedded export-macro class recovery, resolved once and keyed by
3759/// the `ERROR` node that mints each set.
3760///
3761/// [`recover_embedded_function_like_export_classes`] collects and sorts an
3762/// `ERROR` node's whole subtree, and the declaration-strength question asks it
3763/// once per class-like unit in the file. On a translation unit the parser could
3764/// not recover -- Catch2's 449 KB `extras/catch_amalgamated.cpp`, whose `ERROR`
3765/// node spans most of the file -- that is one full subtree pass per class,
3766/// quadratic in the file's size, and it was 78% of that file's inverse scan
3767/// (#1496).
3768///
3769/// Keyed by byte span rather than node identity, so one analyzer generation's
3770/// re-parses of the same content share the index. A nested `ERROR` that shares
3771/// its parent's span recovers the same classes: the only node the parent adds
3772/// is the `ERROR` itself, and no part of a recovered class is an `ERROR`.
3773#[derive(Default)]
3774pub struct CppRecoveredExportClassIndex {
3775    by_error_node: HashMap<(usize, usize), Vec<RecoveredEmbeddedFunctionLikeExportClass>>,
3776}
3777
3778impl CppRecoveredExportClassIndex {
3779    pub fn build(root: Node<'_>, source: &str) -> Self {
3780        let mut by_error_node: HashMap<
3781            (usize, usize),
3782            Vec<RecoveredEmbeddedFunctionLikeExportClass>,
3783        > = HashMap::default();
3784        // One cursor for the whole-file walk, and the error question asked by
3785        // symbol rather than by `kind()` string (#3097).
3786        let mut cursor = root.walk();
3787        let mut stack = vec![root];
3788        while let Some(node) = stack.pop() {
3789            if node.is_error() {
3790                let recovered = recover_embedded_function_like_export_classes(node, source);
3791                if !recovered.is_empty() {
3792                    by_error_node.insert((node.start_byte(), node.end_byte()), recovered);
3793                }
3794            }
3795            stack.extend(node.named_children(&mut cursor));
3796        }
3797        Self { by_error_node }
3798    }
3799
3800    /// The bytes this index holds, for the analyzer cache's weight.
3801    pub fn approximate_size(&self) -> usize {
3802        self.by_error_node
3803            .values()
3804            .fold(0usize, |total, recovered| {
3805                recovered.iter().fold(
3806                    total.saturating_add(std::mem::size_of::<(usize, usize)>()),
3807                    |acc, class| {
3808                        acc.saturating_add(std::mem::size_of::<
3809                            RecoveredEmbeddedFunctionLikeExportClass,
3810                        >())
3811                        .saturating_add(class.name.len())
3812                        .saturating_add(class.raw_supertypes.iter().map(String::len).sum::<usize>())
3813                    },
3814                )
3815            })
3816    }
3817
3818    fn claims(&self, node: Node<'_>, identifier: &str, range: &Range) -> bool {
3819        self.by_error_node
3820            .get(&(node.start_byte(), node.end_byte()))
3821            .is_some_and(|recovered| {
3822                recovered.iter().any(|class| {
3823                    class.name == identifier
3824                        && class.range.start_byte == range.start_byte
3825                        && class.range.end_byte == range.end_byte
3826                })
3827            })
3828    }
3829}
3830
3831// #1496: `recovered_class_body_node_visits_for_test` counts every AST node
3832// `recovered_class_body_at` pops while deciding whether a recovered class shape
3833// claims one declaration range. The count is deterministic for a given source,
3834// so `recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file`
3835// pins it directly instead of timing the walk, the way #2358 pinned the
3836// `remove_code_unit` scan.
3837#[cfg(any(test, feature = "test-support"))]
3838thread_local! {
3839    static RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST: std::cell::Cell<usize> =
3840        const { std::cell::Cell::new(0) };
3841}
3842
3843/// Test-only count of the AST nodes [`recovered_class_body_at`] has visited on
3844/// the calling thread since the last
3845/// [`reset_recovered_class_body_node_visits_for_test`]. See #1496.
3846#[cfg(any(test, feature = "test-support"))]
3847#[doc(hidden)]
3848pub fn recovered_class_body_node_visits_for_test() -> usize {
3849    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(std::cell::Cell::get)
3850}
3851
3852/// Resets the counter read by [`recovered_class_body_node_visits_for_test`].
3853#[cfg(any(test, feature = "test-support"))]
3854#[doc(hidden)]
3855pub fn reset_recovered_class_body_node_visits_for_test() {
3856    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(0));
3857}
3858
3859#[cfg(any(test, feature = "test-support"))]
3860fn record_recovered_class_body_visit() {
3861    RECOVERED_CLASS_BODY_NODE_VISITS_FOR_TEST.with(|cell| cell.set(cell.get() + 1));
3862}
3863
3864#[cfg(not(any(test, feature = "test-support")))]
3865fn record_recovered_class_body_visit() {}
3866
3867/// Whether a recovered class shape named `identifier` owns `range`, and if so
3868/// whether that shape has a body.
3869///
3870/// `Some(true)` is a complete recovered definition, `Some(false)` a recovered
3871/// forward declaration, and `None` means no recovered shape claims the range,
3872/// so the caller reads the plain `class_specifier` family instead. This is the
3873/// single definition of "does a recovered class have a body": the resolver's
3874/// declaration-strength answer and the navigation occurrence role both read it,
3875/// so an export-macro class is a definition on both paths.
3876///
3877/// The walk follows only the nodes whose span covers `range.start_byte`, which
3878/// is every node that can answer. Each recovered shape reports a range that
3879/// starts at the node's own start byte (the function-like export pair, whose
3880/// range is `node.start_byte()..sibling.end_byte()`, and the fragmented plain
3881/// class, which the caller gates on an equal start) or at a token inside the
3882/// node (the embedded export class, keyed on its `class` token, and the
3883/// exported class wrapper, gated on containment here), and every one of them is
3884/// accepted only on an exact match with `range`. Descending everywhere instead
3885/// made one declaration-strength question cost a full pass over the file, so
3886/// asking it once per reference was quadratic in file size: 80% of the 385 s
3887/// inverse scan of Catch2's 449 KB `extras/catch_amalgamated.cpp` was this walk
3888/// (#1496).
3889pub(crate) fn recovered_class_body_at(
3890    recovered_export_classes: &CppRecoveredExportClassIndex,
3891    root: Node<'_>,
3892    source: &str,
3893    identifier: &str,
3894    range: &Range,
3895) -> Option<bool> {
3896    let covers_range_start = |node: &Node<'_>| {
3897        node.start_byte() <= range.start_byte
3898            && (range.start_byte < node.end_byte() || node.start_byte() == range.start_byte)
3899    };
3900    let mut stack = vec![root];
3901    let mut saw_forward = false;
3902    while let Some(node) = stack.pop() {
3903        record_recovered_class_body_visit();
3904        // The pair's recovered range is `node.start_byte()..sibling.end_byte()`,
3905        // so an unequal start settles it before the recovery reads the node's
3906        // children at all.
3907        if (node.start_byte() == range.start_byte
3908            && recovered_function_like_export_class_pair_has_body(node, source, identifier, range))
3909            || recovered_export_classes.claims(node, identifier, range)
3910            || (node.start_byte() == range.start_byte
3911                && recovered_fragmented_class_has_body(node, source, identifier, range))
3912        {
3913            return Some(true);
3914        }
3915        if recovered_collapsed_aggregate_has_body(node, source, identifier, range) {
3916            return Some(true);
3917        }
3918        // Macro-decorated exported classes are recovered from a malformed
3919        // function_definition/declaration wrapper. Their indexed class range starts at
3920        // the displaced class name, while the wrapper starts at `class EXPORT`; recovery
3921        // may also extend the indexed range beyond the wrapper through trailing class
3922        // fragments. Match the structured container that owns the range start by its
3923        // recovered name instead of requiring identical boundaries.
3924        if node.start_byte() <= range.start_byte
3925            && range.start_byte < node.end_byte()
3926            && let Some(has_body) = recovered_exported_class_has_body(node, source, identifier)
3927        {
3928            if has_body {
3929                return Some(true);
3930            }
3931            saw_forward = true;
3932            continue;
3933        }
3934        let mut cursor = node.walk();
3935        stack.extend(node.named_children(&mut cursor).filter(covers_range_start));
3936    }
3937    saw_forward.then_some(false)
3938}
3939
3940/// Whether the collapsed-aggregate recovery reads `node`'s children as the
3941/// head of `identifier`'s member list at `range`.
3942///
3943/// An aggregate whose member list opens with a field-list macro invocation
3944/// never forms a specifier, so the ordinary strength read finds no specifier
3945/// node at the indexed range and answers `Unknown`. A complete definition
3946/// recovered this way then loses the defining-type choice to a forward
3947/// declaration of the same tag in another header, and every member the
3948/// definition declares becomes unreachable through it: libuv's
3949/// `struct uv_loop_s` in `include/uv.h` lost to `struct uv_loop_s;` in
3950/// `include/uv/unix.h`, hiding `uv_loop_s.wq` (#3098). The recovery already
3951/// proves the body, so it answers for its own ranges.
3952fn recovered_collapsed_aggregate_has_body(
3953    node: Node<'_>,
3954    source: &str,
3955    identifier: &str,
3956    range: &Range,
3957) -> bool {
3958    let claims = |head: &CppCollapsedAggregateHead<'_>| {
3959        head.key.start_byte() == range.start_byte
3960            && normalize_cpp_whitespace(node_text(head.name, source)) == identifier
3961    };
3962    if cpp_folded_aggregate_head(node, source).is_some_and(|head| claims(&head)) {
3963        return true;
3964    }
3965    if !node.is_error() {
3966        return false;
3967    }
3968    let mut cursor = node.walk();
3969    let children = node.children(&mut cursor).collect::<Vec<_>>();
3970    children
3971        .iter()
3972        .enumerate()
3973        .filter(|(_, child)| {
3974            child.start_byte() <= range.start_byte && range.start_byte < child.end_byte()
3975        })
3976        .any(|(index, _)| {
3977            cpp_collapsed_aggregate_head(&children, index, source).is_some_and(|head| claims(&head))
3978        })
3979}
3980
3981/// Whether `node` is the base type displaced into the declarator field of an
3982/// export-macro class that tree-sitter represented as a declaration or
3983/// function definition.
3984///
3985/// Declaration extraction already recovers this exact malformed envelope as a
3986/// class and records the declarator as its base. Reference extraction must use
3987/// the same structural fact instead of treating the node as a function name.
3988pub fn is_recovered_exported_class_base_type_node(node: Node<'_>, source: &str) -> bool {
3989    if !matches!(
3990        node.kind(),
3991        "qualified_identifier" | "scoped_type_identifier" | "template_type"
3992    ) {
3993        return false;
3994    }
3995    if let Some(function) = node.parent().filter(|parent| {
3996        parent.kind() == "function_definition"
3997            && parent
3998                .child_by_field_name("declarator")
3999                .is_some_and(|declarator| same_node(declarator, node))
4000    }) {
4001        return recover_exported_class_function_definition(function, source)
4002            .is_some_and(|(_, _, raw_supertypes, _)| raw_supertypes.is_some());
4003    }
4004    let Some(initializer) = node.parent().filter(|parent| {
4005        parent.kind() == "init_declarator"
4006            && parent
4007                .child_by_field_name("declarator")
4008                .is_some_and(|declarator| same_node(declarator, node))
4009    }) else {
4010        return false;
4011    };
4012    initializer
4013        .parent()
4014        .filter(|parent| parent.kind() == "declaration")
4015        .and_then(|declaration| recover_exported_class_declaration(declaration, source))
4016        .is_some_and(|recovered| recovered.raw_supertypes.is_some())
4017}
4018
4019/// Recover the class item from a region reparse that still carries the
4020/// sentinel's synthetic function envelope.  An unknown class attribute can
4021/// make tree-sitter parse `class ATTR Span { ... }` as a function whose type
4022/// is `class ATTR` and whose declarator is `Span`.  The parser's class node is
4023/// then nested below that function, so direct class-child lookup is not enough.
4024struct CppSentinelReparsedClass<'tree> {
4025    declaration_node: Node<'tree>,
4026    name: String,
4027    body: Node<'tree>,
4028    raw_supertypes: Option<Vec<String>>,
4029    base_facts: Option<Vec<CppBaseSpecifierFact>>,
4030}
4031
4032fn cpp_sentinel_reparsed_leading_template(root: Node<'_>) -> Option<Node<'_>> {
4033    let mut cursor = root.walk();
4034    root.named_children(&mut cursor)
4035        .find(|child| child.kind() != "comment")
4036        .filter(|child| child.kind() == "template_declaration")
4037}
4038
4039fn cpp_sentinel_reparsed_class<'tree>(
4040    root: Node<'tree>,
4041    template_node: Option<Node<'tree>>,
4042    source: &str,
4043    ancestry: &ParentIndex<'tree>,
4044) -> Option<CppSentinelReparsedClass<'tree>> {
4045    let container = template_node.unwrap_or(root);
4046    let mut cursor = container.walk();
4047    for child in container.named_children(&mut cursor) {
4048        if matches!(
4049            child.kind(),
4050            "class_specifier" | "struct_specifier" | "union_specifier"
4051        ) {
4052            let name = class_like_name(child, source, ancestry)?;
4053            let body = cpp_body_node(child)?;
4054            let raw_supertypes = matches!(child.kind(), "class_specifier" | "struct_specifier")
4055                .then(|| extract_cpp_supertypes(child, source));
4056            let base_facts = matches!(child.kind(), "class_specifier" | "struct_specifier")
4057                .then(|| extract_cpp_base_facts(child, source));
4058            return Some(CppSentinelReparsedClass {
4059                declaration_node: child,
4060                name,
4061                body,
4062                raw_supertypes,
4063                base_facts,
4064            });
4065        }
4066        if child.kind() == "declaration"
4067            && let Some(class_node) = first_class_like_child(child)
4068        {
4069            let name = class_like_name(class_node, source, ancestry)?;
4070            let body = cpp_body_node(class_node)?;
4071            let raw_supertypes =
4072                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4073                    .then(|| extract_cpp_supertypes(class_node, source));
4074            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4075                .then(|| extract_cpp_base_facts(class_node, source));
4076            return Some(CppSentinelReparsedClass {
4077                declaration_node: class_node,
4078                name,
4079                body,
4080                raw_supertypes,
4081                base_facts,
4082            });
4083        }
4084        // Only when the nested class item carries its own body. A bodyless
4085        // `class ATTR` -- the type half of `class ATTR Span { ... }` reduced to
4086        // a function definition -- is the export-macro shape recovered by the
4087        // next arm, and must fall through to it rather than abort the search.
4088        if child.kind() == "function_definition"
4089            && let Some(class_node) = first_class_like_child(child)
4090            && let Some(body) = cpp_body_node(class_node)
4091            && let Some(name) = class_like_name(class_node, source, ancestry)
4092        {
4093            let raw_supertypes =
4094                matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4095                    .then(|| extract_cpp_supertypes(class_node, source));
4096            let base_facts = matches!(class_node.kind(), "class_specifier" | "struct_specifier")
4097                .then(|| extract_cpp_base_facts(class_node, source));
4098            return Some(CppSentinelReparsedClass {
4099                declaration_node: class_node,
4100                name,
4101                body,
4102                raw_supertypes,
4103                base_facts,
4104            });
4105        }
4106        if child.kind() == "function_definition"
4107            && let Some((_, name, raw_supertypes, base_facts)) =
4108                recover_exported_class_function_definition(child, source)
4109        {
4110            let body = cpp_body_node(child)?;
4111            return Some(CppSentinelReparsedClass {
4112                declaration_node: child,
4113                name,
4114                body,
4115                raw_supertypes,
4116                base_facts,
4117            });
4118        }
4119    }
4120    None
4121}
4122
4123fn recovered_postfix_export_macro_base(
4124    node: Node<'_>,
4125    type_node: Node<'_>,
4126    declarator: Node<'_>,
4127    source: &str,
4128) -> Option<String> {
4129    recovered_postfix_export_macro_base_node(node, type_node, declarator, source)
4130        .and_then(|base| recovered_malformed_base_name(base, source))
4131}
4132
4133fn recovered_postfix_export_macro_base_node<'tree>(
4134    node: Node<'tree>,
4135    type_node: Node<'tree>,
4136    declarator: Node<'tree>,
4137    source: &str,
4138) -> Option<Node<'tree>> {
4139    let mut cursor = node.walk();
4140    let mut malformed_clauses = node.named_children(&mut cursor).filter(|child| {
4141        child.kind() == "ERROR"
4142            && child.start_byte() >= type_node.end_byte()
4143            && child.end_byte() <= declarator.start_byte()
4144            && postfix_export_macro_inheritance(*child, source)
4145    });
4146    malformed_clauses.next()?;
4147    if malformed_clauses.next().is_some() {
4148        return None;
4149    }
4150    Some(declarator)
4151}
4152
4153fn postfix_export_macro_inheritance(node: Node<'_>, source: &str) -> bool {
4154    let mut macro_count = 0;
4155    let mut colon_count = 0;
4156    let mut access_count = 0;
4157    for index in 0..node.child_count() {
4158        let Some(child) = node.child(index) else {
4159            return false;
4160        };
4161        match child.kind() {
4162            "identifier" | "type_identifier" if child.is_named() => {
4163                let candidate = normalize_cpp_whitespace(node_text(child, source));
4164                if !cpp_export_macro_token(&candidate) {
4165                    return false;
4166                }
4167                macro_count += 1;
4168            }
4169            ":" if !child.is_named() => colon_count += 1,
4170            "public" | "protected" | "private" if !child.is_named() => access_count += 1,
4171            _ => return false,
4172        }
4173    }
4174    macro_count == 1 && colon_count == 1 && access_count == 1
4175}
4176
4177fn recovered_single_base_after_declarator_node<'tree>(
4178    node: Node<'tree>,
4179    declarator: Node<'tree>,
4180    source: &str,
4181) -> Option<Node<'tree>> {
4182    let body_start = node
4183        .child_by_field_name("body")
4184        .map(|body| body.start_byte())
4185        .unwrap_or(node.end_byte());
4186    let mut cursor = node.walk();
4187    let mut bases = node
4188        .named_children(&mut cursor)
4189        .filter(|child| {
4190            child.kind() == "ERROR"
4191                && child.start_byte() >= declarator.end_byte()
4192                && child.end_byte() <= body_start
4193        })
4194        .filter_map(|error| {
4195            displaced_exported_class_name(error, source)?;
4196            let mut error_cursor = error.walk();
4197            error
4198                .named_children(&mut error_cursor)
4199                .find(|child| recovered_malformed_base_name(*child, source).is_some())
4200        });
4201    let base = bases.next()?;
4202    bases.next().is_none().then_some(base)
4203}
4204
4205fn malformed_inheritance_syntax(node: Node<'_>) -> bool {
4206    (0..node.child_count()).any(|index| {
4207        node.child(index).is_some_and(|child| {
4208            child.kind() == ":" || cpp_is_inheritance_access_specifier(child.kind())
4209        })
4210    })
4211}
4212
4213fn cpp_is_inheritance_access_specifier(name: &str) -> bool {
4214    matches!(name, "public" | "protected" | "private")
4215}
4216
4217pub fn is_recovered_exported_class_container(node: Node<'_>, source: &str) -> bool {
4218    recover_exported_class_function_definition(node, source).is_some()
4219}
4220
4221fn preserves_declaration_scope_through_wrapper(kind: &str, in_class_scope: bool) -> bool {
4222    matches!(
4223        kind,
4224        "ERROR"
4225            | "preproc_if"
4226            | "preproc_ifdef"
4227            | "preproc_ifndef"
4228            | "preproc_else"
4229            | "preproc_elif"
4230    ) || (kind == "labeled_statement" && in_class_scope)
4231}
4232
4233pub fn is_direct_recovered_exported_class_field_declaration(node: Node<'_>, source: &str) -> bool {
4234    if node.kind() != "declaration" {
4235        return false;
4236    }
4237    let mut ancestor = node.parent();
4238    while let Some(container) = ancestor {
4239        match container.kind() {
4240            "compound_statement" => {
4241                return container.parent().is_some_and(|class_container| {
4242                    is_recovered_exported_class_container(class_container, source)
4243                });
4244            }
4245            // These containers preserve ScopeInfo in visit_node. declaration_list is
4246            // the body container selected for a linkage specification.
4247            "template_declaration" | "linkage_specification" | "declaration_list" => {}
4248            kind if preserves_declaration_scope_through_wrapper(kind, true) => {}
4249            _ => return false,
4250        }
4251        ancestor = container.parent();
4252    }
4253    false
4254}
4255
4256fn class_identifier_before_body(node: Node<'_>, source: &str) -> Option<String> {
4257    let body_start = node
4258        .child_by_field_name("body")
4259        .map(|body| body.start_byte())
4260        .unwrap_or(node.end_byte());
4261    let mut stack = Vec::new();
4262    for index in (0..node.named_child_count()).rev() {
4263        let Some(child) = node.named_child(index) else {
4264            continue;
4265        };
4266        if child.start_byte() >= body_start {
4267            continue;
4268        }
4269        stack.push(child);
4270    }
4271
4272    let mut best = None;
4273    while let Some(current) = stack.pop() {
4274        if matches!(current.kind(), "identifier" | "type_identifier") {
4275            let name = normalize_cpp_whitespace(node_text(current, source));
4276            if !name.is_empty()
4277                && !cpp_export_macro_token(&name)
4278                && !matches!(name.as_str(), "class" | "struct" | "union")
4279            {
4280                best = Some(name);
4281            }
4282            continue;
4283        }
4284
4285        for index in (0..current.named_child_count()).rev() {
4286            if let Some(child) = current.named_child(index)
4287                && child.start_byte() < body_start
4288            {
4289                stack.push(child);
4290            }
4291        }
4292    }
4293    best
4294}
4295
4296fn exported_class_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4297    if node.kind() == "declaration"
4298        && node
4299            .child_by_field_name("type")
4300            .or_else(|| first_class_like_child(node))
4301            .is_some_and(|type_node| {
4302                matches!(
4303                    type_node.kind(),
4304                    "class_specifier" | "struct_specifier" | "union_specifier"
4305                )
4306            })
4307        && let Some(name) = node
4308            .child_by_field_name("declarator")
4309            .and_then(|declarator| declarator_name_from_node(declarator, source))
4310        && !cpp_export_macro_token(&name)
4311    {
4312        return Some(name);
4313    }
4314
4315    if node.kind() == "function_definition"
4316        && node.child_by_field_name("type").is_some_and(|type_node| {
4317            matches!(
4318                type_node.kind(),
4319                "class_specifier" | "struct_specifier" | "union_specifier"
4320            )
4321        })
4322        && let Some(name) = node
4323            .child_by_field_name("declarator")
4324            .and_then(|declarator| direct_identifier_name(declarator, source))
4325        && !cpp_export_macro_token(&name)
4326    {
4327        return Some(name);
4328    }
4329
4330    let class_node = if matches!(
4331        node.kind(),
4332        "class_specifier" | "struct_specifier" | "union_specifier"
4333    ) {
4334        node
4335    } else {
4336        first_class_like_child(node)?
4337    };
4338    class_like_name_from_children(class_node, source)
4339}
4340
4341/// Normalize the text of a directly selected C++ identifier node.
4342pub fn direct_identifier_name(node: Node<'_>, source: &str) -> Option<String> {
4343    if !matches!(
4344        node.kind(),
4345        "identifier" | "field_identifier" | "type_identifier"
4346    ) {
4347        return None;
4348    }
4349    let name = normalize_cpp_whitespace(node_text(node, source));
4350    (!name.is_empty()).then_some(name)
4351}
4352
4353fn declarator_name_from_node(node: Node<'_>, source: &str) -> Option<String> {
4354    if let Some(name) = direct_identifier_name(node, source) {
4355        return Some(name);
4356    }
4357    let mut cursor = node.walk();
4358    node.named_children(&mut cursor)
4359        .find_map(|child| declarator_name_from_node(child, source))
4360}
4361
4362fn first_class_like_child(node: Node<'_>) -> Option<Node<'_>> {
4363    let mut cursor = node.walk();
4364    node.named_children(&mut cursor).find(|child| {
4365        matches!(
4366            child.kind(),
4367            "class_specifier" | "struct_specifier" | "union_specifier"
4368        )
4369    })
4370}
4371
4372/// Push a container's children as a `Siblings` cursor rather than snapshotting
4373/// them all with one shared scope: children are visited one at a time so a
4374/// `using namespace X;` sibling can affect the scope threaded to the siblings
4375/// that textually follow it (issue #1093).
4376fn push_cpp_container_work<'tree>(
4377    node: Node<'tree>,
4378    scope: ScopeInfo,
4379    stack: &mut Vec<CppWork<'tree>>,
4380) {
4381    push_cpp_sibling_range(node, 0, usize::MAX, scope, stack);
4382}
4383
4384/// Materialize one selected named-child range with a tree-sitter cursor. The
4385/// cursor advances linearly across the parent's concrete children; repeatedly
4386/// asking for `named_child(index)` is quadratic on very wide generated nodes.
4387fn push_cpp_sibling_range<'tree>(
4388    parent: Node<'tree>,
4389    start_index: usize,
4390    end_index: usize,
4391    scope: ScopeInfo,
4392    stack: &mut Vec<CppWork<'tree>>,
4393) {
4394    let mut cursor = parent.walk();
4395    let children = parent
4396        .named_children(&mut cursor)
4397        .skip(start_index)
4398        .take(end_index.saturating_sub(start_index))
4399        .collect::<Vec<_>>()
4400        .into_iter();
4401    stack.push(CppWork::Siblings(CppSiblingsWork { children, scope }));
4402}
4403
4404/// Advance a `Siblings` cursor by one child: dispatch the current child under
4405/// the scope accumulated from its *earlier* siblings, then push a
4406/// continuation for the remaining siblings carrying the scope updated for
4407/// *this* child (only `using namespace X;` directives change it). Pushing the
4408/// continuation before the current child's own node work means the current
4409/// child's subtree fully drains (LIFO) before the next sibling is visited,
4410/// preserving left-to-right order.
4411fn advance_cpp_siblings<'tree>(
4412    mut siblings: CppSiblingsWork<'tree>,
4413    source: &str,
4414    stack: &mut Vec<CppWork<'tree>>,
4415) {
4416    let Some(child) = siblings.children.next() else {
4417        return;
4418    };
4419    let current_scope = siblings.scope.clone();
4420    if let Some(namespace) = cpp_using_namespace_target(child, source) {
4421        siblings.scope.visible_using_namespaces.push(namespace);
4422    }
4423    push_cpp_using_declaration_binding(&mut siblings.scope, child, source);
4424    if !siblings.children.as_slice().is_empty() {
4425        stack.push(CppWork::Siblings(siblings));
4426    }
4427    stack.push(CppWork::Node(CppNodeWork {
4428        node: child,
4429        scope: current_scope,
4430    }));
4431}
4432
4433/// The namespace target of a `using namespace X;` directive, or `None` for
4434/// any other `using_declaration` shape (`using X;`, `using X::Y;`) or node
4435/// kind. Distinguished structurally by the presence of the grammar's literal
4436/// `namespace` keyword token among the node's children -- not by inspecting
4437/// source text -- so it never misreads a member-importing using-declaration
4438/// as a namespace directive.
4439fn cpp_using_namespace_target(node: Node<'_>, source: &str) -> Option<String> {
4440    if node.kind() != "using_declaration" {
4441        return None;
4442    }
4443    let mut cursor = node.walk();
4444    let is_namespace_directive = node
4445        .children(&mut cursor)
4446        .any(|child| child.kind() == "namespace");
4447    if !is_namespace_directive {
4448        return None;
4449    }
4450    let target = node.named_child(0)?;
4451    // A leading `::` is the explicit-global marker, not part of the namespace
4452    // path (`using namespace ::std::chrono;`). Drop that AST token before
4453    // reading the target text, the same boundary `cpp_raw_namespace_name_components`
4454    // keeps: storing the marker verbatim desynced the legacy package string from
4455    // the FqName bridge, which splits on `::` and drops the empty leading
4456    // component, tripping the package/short boundary assert when a bare-owner
4457    // out-of-line definition borrowed the directive's namespace (#1093 path).
4458    let start = target
4459        .child(0)
4460        .filter(|child| !child.is_named() && child.kind() == "::")
4461        .map_or(target.start_byte(), |marker| marker.end_byte());
4462    let text = normalize_cpp_whitespace(
4463        source
4464            .get(start..target.end_byte())
4465            .expect("using-directive target covers one source range"),
4466    );
4467    (!text.is_empty()).then_some(text)
4468}
4469
4470/// Record `node` in `scope` when it is a namespace-scope `using ns::Type;`
4471/// declaration, so later siblings see the binding it introduces.
4472///
4473/// The target is read from the declaration's structured qualified name. A
4474/// using directive (`using namespace ns;`), a `using enum`, a member using
4475/// inside a class body, and a target with a template-id component introduce
4476/// no class binding this extraction can use, so they are skipped. A relative
4477/// target inside a namespace resolves against that namespace, dropping a
4478/// prefix that re-states it, the same reading `split_cpp_name` gives a
4479/// written out-of-line qualifier: a member definition must sit in a namespace
4480/// that encloses its class.
4481fn push_cpp_using_declaration_binding(scope: &mut ScopeInfo, node: Node<'_>, source: &str) {
4482    if node.kind() != "using_declaration" || scope.class_unit.is_some() {
4483        return;
4484    }
4485    let mut cursor = node.walk();
4486    if node
4487        .children(&mut cursor)
4488        .any(|child| matches!(child.kind(), "namespace" | "enum"))
4489    {
4490        return;
4491    }
4492    let Some((components, explicitly_global)) = node
4493        .named_child(0)
4494        .and_then(|target| structured_cpp_qualified_components(target, source))
4495    else {
4496        return;
4497    };
4498    let Some((name, namespace)) = components.split_last() else {
4499        return;
4500    };
4501    if namespace.is_empty() || components.iter().any(|component| component.is_template_id) {
4502        return;
4503    }
4504    let namespace = namespace
4505        .iter()
4506        .map(|component| component.name.as_str())
4507        .collect::<Vec<_>>();
4508    let target_package = if explicitly_global || scope.package_name.is_empty() {
4509        namespace.join("::")
4510    } else {
4511        let relative = strip_redundant_namespace_prefix(&namespace, &scope.package_name);
4512        std::iter::once(scope.package_name.as_str())
4513            .chain(relative.iter().copied())
4514            .collect::<Vec<_>>()
4515            .join("::")
4516    };
4517    Arc::make_mut(&mut scope.visible_using_declarations).push(CppUsingDeclarationBinding {
4518        scope_package: scope.package_name.clone(),
4519        name: name.name.clone(),
4520        target_package,
4521    });
4522}
4523
4524/// Tokens that open a construct only a matching later token closes.
4525const CPP_CONSTRUCT_OPENERS: [&str; 6] = ["#if", "#ifdef", "#ifndef", "{", "(", "["];
4526
4527/// The opening tokens of the constructs a C/C++ parse left open at the end of
4528/// the file, in source order (#3501). Empty unless the tree's root is an ERROR
4529/// node.
4530///
4531/// The grammar has no preprocessor, so a construct opened in one conditional
4532/// branch and closed in another stays open: `#if 0` / `extern "C" {` /
4533/// `#endif` leaves its `{` unmatched until a later `#if 0` / `}` / `#endif`.
4534/// One such pair repairs with a single MISSING token. With more of them, the
4535/// parse version tree-sitter keeps reaches the end of input in its error
4536/// state, and tree-sitter wraps every subtree on that version's stack in one
4537/// ERROR node and returns it as the root (`recover_eof` in its `parser.c`).
4538/// The root's children are then exactly that stack: completed subtrees, and
4539/// the bare tokens of the constructs still waiting to be reduced when input
4540/// ran out. A reduced construct is a node of its own, so a bare opener among
4541/// the root's children is one the parse never closed. tdlib/td's copy of the
4542/// SQLite amalgamation parses this way: its root spans all 8,274,268 bytes,
4543/// and its outermost open construct is the `#ifndef SQLITEINT_H` include guard
4544/// at byte 19,718.
4545pub fn cpp_unclosed_construct_openers<'tree>(root: Node<'tree>) -> Vec<Node<'tree>> {
4546    assert!(
4547        root.parent().is_none(),
4548        "only a tree's root records what its parse left open"
4549    );
4550    if !root.is_error() {
4551        return Vec::new();
4552    }
4553    children_iter(root)
4554        .filter(|child| CPP_CONSTRUCT_OPENERS.contains(&child.kind()))
4555        .collect()
4556}
4557
4558/// Record every declaration of `parsed` that has a range inside `recovery`
4559/// as recovered, unless a narrower recovery already claimed it, and mark the
4560/// reading partial when any declaration lies there (#3501).
4561///
4562/// `recovery` runs from the outermost construct the parse left open to the
4563/// end of the file. Everything before that construct was completed at file
4564/// scope, as in any parse. Everything after it sat on the parse stack above
4565/// an open construct when input ran out, so the scope and nesting the walk
4566/// gave it are a recovery claim rather than a reading of a completed tree,
4567/// and the file's analysis is partial whichever recovery recorded it.
4568fn record_declarations_left_open(parsed: &mut ParsedFile, recovery: Range) {
4569    let recorded: HashSet<CodeUnit> = parsed
4570        .materialization_records
4571        .iter()
4572        .filter_map(|record| match record {
4573            MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.clone()),
4574            _ => None,
4575        })
4576        .collect();
4577    let mut left_open: Vec<(usize, CodeUnit)> = parsed
4578        .declarations()
4579        .iter()
4580        .filter_map(|unit| {
4581            let ranges = parsed.declaration_ranges(unit);
4582            let first = ranges.iter().map(|range| range.start_byte).min()?;
4583            let last = ranges.iter().map(|range| range.start_byte).max()?;
4584            (last >= recovery.start_byte).then(|| (first, unit.clone()))
4585        })
4586        .collect();
4587    if left_open.is_empty() {
4588        return;
4589    }
4590    parsed.completeness = ParseCompleteness::Partial;
4591    left_open.retain(|(_, unit)| !recorded.contains(unit));
4592    left_open.sort();
4593    for (_, unit) in left_open {
4594        parsed
4595            .record_materialization(MaterializationRecord::RecoveredDeclaration { recovery, unit });
4596    }
4597}
4598
4599/// Extract all requested dialect readings from one primary AST event stream.
4600/// Declaration schedulers retain their own admission, recovery and sibling order;
4601/// structural facts, includes and identifiers are interpreted once for the tree.
4602pub(crate) fn parse_cpp_readings<'tree>(
4603    file: &ProjectFile,
4604    source: &str,
4605    root: Node<'tree>,
4606    ancestry: &ParentIndex<'tree>,
4607    dialects: &[LanguageDialect],
4608) -> Vec<ParsedFile> {
4609    parse_cpp_readings_with_object_macro_fields(
4610        file,
4611        source,
4612        root,
4613        ancestry,
4614        dialects,
4615        HashMap::default(),
4616    )
4617}
4618
4619pub(crate) fn parse_cpp_readings_with_object_macro_fields<'tree>(
4620    file: &ProjectFile,
4621    source: &str,
4622    root: Node<'tree>,
4623    ancestry: &ParentIndex<'tree>,
4624    dialects: &[LanguageDialect],
4625    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4626) -> Vec<ParsedFile> {
4627    parse_cpp_readings_observed(
4628        file,
4629        source,
4630        root,
4631        ancestry,
4632        dialects,
4633        object_macro_fields,
4634        &IgnoreCppCallableObservations,
4635    )
4636}
4637
4638/// A live callable observation has the exact physical declaration range already
4639/// linked by the visitor. The observer may copy facts, but cannot retain nodes.
4640pub(crate) trait CppCallableObserver {
4641    fn observe(
4642        &self,
4643        unit: &CodeUnit,
4644        declaration_start: usize,
4645        metadata: &SignatureMetadata,
4646        declarator: Node<'_>,
4647        source: &str,
4648        ancestry: &ParentIndex<'_>,
4649    );
4650
4651    fn observe_duplicate(
4652        &self,
4653        _unit: &CodeUnit,
4654        _declaration_start: usize,
4655        _declarator: Node<'_>,
4656        _source: &str,
4657        _ancestry: &ParentIndex<'_>,
4658        _metadata: &dyn Fn() -> SignatureMetadata,
4659    ) {
4660    }
4661}
4662
4663struct IgnoreCppCallableObservations;
4664
4665impl CppCallableObserver for IgnoreCppCallableObservations {
4666    fn observe(
4667        &self,
4668        _unit: &CodeUnit,
4669        _declaration_start: usize,
4670        _metadata: &SignatureMetadata,
4671        _declarator: Node<'_>,
4672        _source: &str,
4673        _ancestry: &ParentIndex<'_>,
4674    ) {
4675    }
4676}
4677
4678pub(crate) fn parse_cpp_readings_observed<'tree>(
4679    file: &ProjectFile,
4680    source: &str,
4681    root: Node<'tree>,
4682    ancestry: &ParentIndex<'tree>,
4683    dialects: &[LanguageDialect],
4684    object_macro_fields: HashMap<String, ObjectMacroReplacement>,
4685    callable_observer: &dyn CppCallableObserver,
4686) -> Vec<ParsedFile> {
4687    assert!(
4688        dialects.len() == 1
4689            || (dialects.len() == 2
4690                && dialects[0] != LanguageDialect::CppC
4691                && dialects[1] == LanguageDialect::CppC),
4692        "one primary dialect, optionally followed by its C alternative"
4693    );
4694    let collector = RefCell::new(PrimarySourceFactCollector::new(source));
4695    let properties = RefCell::new(HashMap::default());
4696    let dependencies = RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
4697    let orphaned_namespaces = OrphanedNamespaceScopeIndex::build(root, source);
4698    let mut using_namespaces = Vec::new();
4699    let mut namespace_aliases = Vec::new();
4700    let mut readings: Vec<_> = dialects
4701        .iter()
4702        .map(|_| ParsedFile::new(String::new()))
4703        .collect();
4704    let mut visitors: Vec<_> = readings
4705        .iter_mut()
4706        .zip(dialects)
4707        .map(|(parsed, dialect)| CppVisitor {
4708            file,
4709            source,
4710            parsed,
4711            source_collector: &collector,
4712            source_properties: &properties,
4713            source_dependencies: &dependencies,
4714            primary_ancestry: ancestry,
4715            primary_root_id: root.id(),
4716            callable_observer,
4717            source_links: HashMap::default(),
4718            source_declarations: HashSet::default(),
4719            preprocessor_context_cache: Default::default(),
4720            c_tag_semantics: *dialect == LanguageDialect::CppC,
4721            c_tag_scope_witness: false,
4722            recovered_class_sibling_scopes: HashMap::default(),
4723            consumed_fragment_regions: Vec::new(),
4724            orphaned_namespaces: &orphaned_namespaces,
4725            partitioned_regions: Vec::new(),
4726            namespace_forward_scans: HashMap::default(),
4727            recovery_captures: Vec::new(),
4728            active_source_context: None,
4729            object_macro_fields: object_macro_fields.clone(),
4730            ambiguous_object_macro_fields: HashSet::default(),
4731        })
4732        .collect();
4733    let mut declaration_stacks: Vec<_> = dialects
4734        .iter()
4735        .map(|_| {
4736            vec![CppWork::Container(CppContainer {
4737                node: root,
4738                scope: ScopeInfo {
4739                    package_name: String::new(),
4740                    module: None,
4741                    class_unit: None,
4742                    template_signature: None,
4743                    template_metadata: None,
4744                    declarations_are_fields: false,
4745                    recovered_specialization_member_scope: false,
4746                    visible_using_namespaces: Vec::new(),
4747                    local_block: None,
4748                    visible_using_declarations: Arc::default(),
4749                },
4750            })]
4751        })
4752        .collect();
4753    let kinds = CompiledKinds::compile(
4754        &tree_sitter_cpp::LANGUAGE.into(),
4755        CPP_STRUCTURAL_SPEC.kind_table(),
4756    );
4757    // Forward macro names affect calls before their definitions and require the
4758    // existing bounded preprocessor-only context prepass.
4759    let context = CPP_STRUCTURAL_SPEC.call_site_context(root, source);
4760    let mut structural = StructuralFactCollector::new(
4761        &CPP_STRUCTURAL_SPEC,
4762        source,
4763        &context,
4764        brokk_bifrost_core::analyzer::tree_walk::ParentIndex::new(root),
4765        usize::MAX,
4766        None,
4767    );
4768    let mut imports = Vec::new();
4769    let mut includes = Vec::new();
4770    let mut expressions = Vec::new();
4771    let named_record_sizes = crate::source_facts::named_record_size_index(root, source);
4772    let mut statement_facts = StatementFactRows::default();
4773    let mut identifiers = HashSet::default();
4774    let mut entered = HashSet::default();
4775    let mut primary_ordinals = HashMap::default();
4776    let mut stack = vec![(root, None)];
4777    while let Some((node, parent)) = stack.pop() {
4778        assert!(entered.insert(node.id()), "one primary event per AST node");
4779        primary_ordinals.insert(node.id(), primary_ordinals.len());
4780        dependencies.borrow_mut().primary.enter(node, source);
4781        let mut child_parent = parent;
4782        if node.is_named() {
4783            crate::structural::capture_statement_fact_rows(node, source, &mut statement_facts);
4784            if let Some(namespace) = cpp_using_namespace_target(node, source) {
4785                using_namespaces.push((collector.borrow_mut().intern_node(node), namespace));
4786            }
4787            if let Some(alias) = crate::source_facts::capture_namespace_alias(
4788                node,
4789                source,
4790                ancestry,
4791                &orphaned_namespaces,
4792                &mut collector.borrow_mut(),
4793            ) {
4794                namespace_aliases.push(alias);
4795            }
4796            if let Some(raw_kind) = kinds.kind_of(&node)
4797                && CPP_STRUCTURAL_SPEC.should_extract(node, raw_kind)
4798            {
4799                let kind = CPP_STRUCTURAL_SPEC.refine_kind(
4800                    node,
4801                    raw_kind,
4802                    parent.map(|id| structural.normalized_kind(id)),
4803                    source,
4804                    &context,
4805                );
4806                let mut source_facts = collector.borrow_mut();
4807                let id = structural
4808                    .enter(node, kind, parent, &mut source_facts)
4809                    .expect("unbounded C++ structural collector");
4810                let mut sink = structural.role_sink(&mut source_facts);
4811                CPP_STRUCTURAL_SPEC.extract(node, kind, &mut sink);
4812                structural
4813                    .accept_roles(id, sink.into_parts())
4814                    .expect("unbounded C++ structural roles");
4815                child_parent = Some(id);
4816            }
4817            if let Some(fact) = crate::source_facts::capture_expression_fact(
4818                node,
4819                source,
4820                collector.borrow_mut().intern_node(node),
4821                &named_record_sizes,
4822            ) {
4823                expressions.push(fact);
4824            }
4825            if node.kind() == "preproc_include" {
4826                let raw = normalize_cpp_whitespace(node_text(node, source));
4827                if !raw.is_empty() {
4828                    let occurrence = collector.borrow_mut().intern_node(node);
4829                    let target = node.child_by_field_name("path").and_then(|path| {
4830                        let (value, quoted) = crate::imports::include_path_from_node(node, source)?;
4831                        let target = collector.borrow_mut().intern_node(path);
4832                        includes.push(brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
4833                            declaration: occurrence,
4834                            target,
4835                            path: value.to_owned(),
4836                            quoted,
4837                        });
4838                        Some(target)
4839                    });
4840                    imports.push(SourceImportFact::from_import(
4841                        cpp_include_info(raw),
4842                        occurrence,
4843                        target,
4844                        None,
4845                        Vec::new(),
4846                    ));
4847                }
4848            }
4849            if matches!(
4850                node.kind(),
4851                "type_identifier" | "identifier" | "qualified_identifier"
4852            ) {
4853                let text = node_text(node, source).trim();
4854                if !text.is_empty() {
4855                    identifiers.insert(text.to_owned());
4856                }
4857            }
4858        }
4859        for index in 0..visitors.len() {
4860            // Delay the alternative policy until the primary walk encounters a
4861            // tag whose scope can differ. Its saved declaration stack catches up
4862            // through already-entered events, including recovery, without a
4863            // second AST traversal. A false witness performs no C declaration work.
4864            if index != 0 && !visitors[0].c_tag_scope_witness {
4865                continue;
4866            }
4867            let visitor = &mut visitors[index];
4868            advance_primary_declarations(
4869                visitor,
4870                &mut declaration_stacks[index],
4871                &entered,
4872                ancestry,
4873            );
4874            visitor.drain_partitioned_regions();
4875        }
4876        let mut cursor = node.walk();
4877        let children: Vec<_> = node
4878            .children(&mut cursor)
4879            .map(|child| (child, child_parent))
4880            .collect();
4881        stack.extend(children.into_iter().rev());
4882    }
4883    let reading_count = if visitors[0].c_tag_scope_witness {
4884        visitors.len()
4885    } else {
4886        1
4887    };
4888    visitors.truncate(reading_count);
4889    declaration_stacks.truncate(reading_count);
4890    assert!(
4891        declaration_stacks.iter().all(Vec::is_empty),
4892        "all primary declaration events were consumed"
4893    );
4894    for visitor in &mut visitors {
4895        visitor.parsed.source_declaration_units = std::mem::take(&mut visitor.source_links)
4896            .into_iter()
4897            .flat_map(|(unit, declarations)| {
4898                declarations
4899                    .into_iter()
4900                    .map(move |declaration| (declaration, unit.clone()))
4901            })
4902            .collect();
4903    }
4904    let dialect_declarations: Vec<_> = visitors
4905        .iter()
4906        .map(|visitor| visitor.source_declarations.clone())
4907        .collect();
4908    drop(visitors);
4909    readings.truncate(reading_count);
4910    dependencies
4911        .borrow_mut()
4912        .finish_primary(&mut properties.borrow_mut());
4913    // Preserve the existing malformed quoted-directive recovery, but publish its
4914    // explicit source occurrence into the same import family as parsed directives.
4915    recover_quoted_source_imports(source, &mut imports, &mut includes, &collector);
4916    let primary_occurrence_ordinals: HashMap<_, _> = collector
4917        .borrow()
4918        .primary_node_occurrences()
4919        .map(|(node, occurrence)| (occurrence, primary_ordinals[&node]))
4920        .collect();
4921    let occurrences = collector.into_inner().finish();
4922    let generic_imports: Vec<_> = (0..imports.len())
4923        .map(|i| SourceImportId::try_from_index(i).expect("C++ import id"))
4924        .collect();
4925    let source_facts = ParsedSourceFacts {
4926        cpp: Some(brokk_bifrost_core::analyzer::cpp_facts::CppSourceFacts {
4927            includes,
4928            declarations: {
4929                let mut facts: Vec<_> = properties.into_inner().into_values().collect();
4930                facts.sort_by_key(|fact| fact.declaration.get());
4931                facts
4932            },
4933            using_namespaces,
4934            namespace_aliases,
4935            expressions,
4936            statement_facts,
4937        }),
4938        go: None,
4939        java: None,
4940        js_ts: None,
4941        php: None,
4942        scala: None,
4943        ruby: None,
4944        python: None,
4945        source_bytes: source.len(),
4946        occurrences,
4947        structural: structural
4948            .finish(root)
4949            .expect("C++ structural collection finishes"),
4950        native_site_occurrences: Vec::new(),
4951        native_declaration_sources: Vec::new(),
4952        declaration_visibilities: None,
4953        rust_declaration_properties: Vec::new(),
4954        rust_modules: None,
4955        rust_types: Vec::new(),
4956        rust_items: Default::default(),
4957        imports,
4958        generic_imports,
4959        rust_import_contexts: Vec::new(),
4960    };
4961    for (parsed, declarations) in readings.iter_mut().zip(dialect_declarations) {
4962        parsed.finalize_deferred_replacements();
4963        let links = std::mem::take(&mut parsed.source_declaration_units);
4964        parsed.source_declaration_units = links
4965            .into_iter()
4966            .filter(|(_, unit)| parsed.contains_declaration(unit))
4967            .collect();
4968        parsed.imports = source_facts
4969            .imports
4970            .iter()
4971            .map(|import| import.import_info(&source_facts.occurrences))
4972            .collect();
4973        parsed.type_identifiers = identifiers.clone();
4974        parsed.source_facts = Some(crate::source_projection::finalize_dialect(
4975            &source_facts,
4976            &declarations,
4977            &primary_occurrence_ordinals,
4978            &mut parsed.source_declaration_units,
4979            &mut parsed.source_declaration_metadata,
4980        ));
4981    }
4982    if let Some(outermost) = cpp_unclosed_construct_openers(root).first() {
4983        // The recovery runs to the end of the file, which is how a reader of
4984        // the record tells this recovery from a local one (#3501).
4985        let recovery = Range {
4986            start_byte: outermost.start_byte(),
4987            end_byte: source.len(),
4988            start_line: outermost.start_position().row + 1,
4989            end_line: root.end_position().row + 1,
4990        };
4991        for parsed in &mut readings {
4992            record_declarations_left_open(parsed, recovery);
4993        }
4994    }
4995    readings
4996}
4997
4998fn advance_primary_declarations<'tree>(
4999    visitor: &mut CppVisitor<'_, '_>,
5000    stack: &mut Vec<CppWork<'tree>>,
5001    entered: &HashSet<usize>,
5002    ancestry: &ParentIndex<'tree>,
5003) {
5004    while let Some(work) = stack.pop() {
5005        match work {
5006            CppWork::Container(container) => {
5007                push_cpp_container_work(container.node, container.scope, stack)
5008            }
5009            CppWork::Siblings(siblings) => advance_cpp_siblings(siblings, visitor.source, stack),
5010            CppWork::Node(work) => {
5011                if !entered.contains(&work.node.id()) {
5012                    stack.push(CppWork::Node(work));
5013                    return;
5014                }
5015                if !visitor.node_is_inside_consumed_fragment(work.node) {
5016                    visitor.visit_node(work.node, &work.scope, stack, ancestry);
5017                }
5018            }
5019        }
5020    }
5021}
5022
5023/// The preprocessor and scope context a recovered or partitioned region
5024/// inherits from the position it was cut from.
5025///
5026/// The three conditional fields are independent projections of the chain of
5027/// enclosing conditional families, not one state: an inner family without a
5028/// terminal `#else` inside an outer exhaustive one has `family` = inner,
5029/// `exhaustive_family` = outer and `callable_guard_completion_byte` = `None`.
5030#[derive(Clone)]
5031struct CppActiveSourceContext {
5032    /// The guards every enclosing branch requires. `None` when the context
5033    /// keeps the region out of every configuration (a false or unreadable
5034    /// condition, or contradictory branches); an empty set is unguarded.
5035    guards: Option<HashSet<crate::graph::resolver::PreprocessorGuard>>,
5036    namespace: Option<String>,
5037    child_namespace: Option<String>,
5038    outer_lexical_scope: Vec<String>,
5039    child_lexical_scope: Vec<String>,
5040    /// The innermost enclosing conditional family, exhaustive or not.
5041    family: Option<SourceOccurrenceId>,
5042    /// The innermost enclosing family that ends in `#else`.
5043    exhaustive_family: Option<SourceOccurrenceId>,
5044    /// `None` when any enclosing family lacks a terminal `#else`; otherwise
5045    /// the end byte of the enclosing families, `Some(0)` outside all of them.
5046    /// Merging takes the maximum and lets `None` absorb.
5047    callable_guard_completion_byte: Option<usize>,
5048}
5049
5050/// One owned container reparse deferred until the primary declaration walk has
5051/// reached its source position. The tree and context are owned so the deferred
5052/// walk never retains a `Node` borrowed from a temporary recovery tree.
5053pub struct CppPartitionedRegion {
5054    tree: Tree,
5055    range: std::ops::Range<usize>,
5056    scope: ScopeInfo,
5057    recovery: Range,
5058    source_context: Option<CppActiveSourceContext>,
5059}
5060
5061pub struct CppVisitor<'a, 'source> {
5062    pub file: &'a ProjectFile,
5063    pub source: &'source str,
5064    pub parsed: &'a mut ParsedFile,
5065    pub source_collector: &'a RefCell<PrimarySourceFactCollector<'source>>,
5066    source_properties: &'a RefCell<
5067        HashMap<
5068            SourceDeclarationId,
5069            brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5070        >,
5071    >,
5072    source_dependencies: &'a RefCell<crate::source_dependencies::DeclarationDependencies>,
5073    pub primary_ancestry: &'a ParentIndex<'a>,
5074    pub primary_root_id: usize,
5075    callable_observer: &'a dyn CppCallableObserver,
5076    source_links: HashMap<CodeUnit, Vec<SourceDeclarationId>>,
5077    source_declarations: HashSet<SourceDeclarationId>,
5078    preprocessor_context_cache: crate::graph::resolver::PreprocessorContextCache,
5079    /// Whether this translation unit is compiled as C -- the `CppC` dialect of
5080    /// `LanguageDialect`, i.e. an exact lowercase `.c` extension.
5081    ///
5082    /// C has no nested tag scope: a struct/union/enum tag declared inside
5083    /// another aggregate's member list has the scope of the outer declaration
5084    /// itself (C17 6.2.1, 6.7.2.3). `struct outer { struct inner { int v; } i; };`
5085    /// therefore declares a file-scope `inner` that a later file-scope
5086    /// `struct inner *p;` legitimately references, where C++ would make the
5087    /// same shape a nested class `outer::inner`. Headers carry no compilation
5088    /// language of their own and keep the conservative C++ interpretation.
5089    pub c_tag_semantics: bool,
5090    /// Sticky evidence that this C++ walk reached a declaration whose scope
5091    /// would change under C tag semantics, including visitor recovery work.
5092    pub(crate) c_tag_scope_witness: bool,
5093    pub recovered_class_sibling_scopes: HashMap<usize, ScopeInfo>,
5094    /// Byte regions whose contents were re-owned by a fragmented export-class
5095    /// recovery (#938): the scattered members between the fragmented
5096    /// declaration and its displaced closing brace are indexed as members of
5097    /// the recovered class by the region reparse, so the ordinary sibling walk
5098    /// must not ALSO index them as top-level declarations (that double-indexing
5099    /// made a scattered nested class ambiguous between `Inner` and
5100    /// `Widget$Inner`). Regions are rare (one per fragmented recovery), so a
5101    /// linear scan at visit time is fine.
5102    pub consumed_fragment_regions: Vec<(usize, usize)>,
5103    /// Lexical namespace corrections and brace boundaries for declarations
5104    /// displaced by parse recovery (issues #1537 and #3087). The parsed
5105    /// ancestors can stop short or extend past their true closes; see
5106    /// [`CppVisitor::recovered_namespace_scope`].
5107    pub orphaned_namespaces: &'a OrphanedNamespaceScopeIndex,
5108    /// Owned reparses waiting for the outer work loop. Partitioning another
5109    /// swallowed class in a tail must not grow the Rust call stack.
5110    pub partitioned_regions: Vec<CppPartitionedRegion>,
5111    /// The namespace forward declarations already folded out of each tree this
5112    /// walk has asked [`CppVisitor::unique_earlier_namespace_forward`] about.
5113    /// Empty until the first question, which the overwhelming majority of files
5114    /// never ask.
5115    pub namespace_forward_scans: HashMap<CppTreeIdentity, CppNamespaceForwardScan>,
5116    /// What each open [`CppVisitor::record_recovered_declarations`] has watched
5117    /// happen to the declaration set, innermost last. Empty outside a recovery
5118    /// reparse, which is almost always (#2787).
5119    pub recovery_captures: Vec<CppRecoveryCapture>,
5120    active_source_context: Option<CppActiveSourceContext>,
5121    /// Object-like field-list macros defined earlier in this source. Their
5122    /// replacements are parsed structurally and materialized under each
5123    /// invoking aggregate; no source-text expansion is used.
5124    pub object_macro_fields: HashMap<String, ObjectMacroReplacement>,
5125    /// Names whose active replacement is not a unique structured field list.
5126    /// A later `#undef` resets the ambiguity; another `#define` does not.
5127    pub ambiguous_object_macro_fields: HashSet<String>,
5128}
5129
5130/// One source-order event from an object-like field-list macro environment.
5131///
5132/// The event stream lets an include-closure consumer preserve preprocessor
5133/// invalidation without guessing which parsed header happened to be visited
5134/// first. A conservative consumer may permanently block a name after an
5135/// `undef` when conditional include order is not provable.
5136#[derive(Clone, Debug, PartialEq, Eq)]
5137pub enum ObjectMacroFieldEvent {
5138    Define {
5139        name: String,
5140        replacement: ObjectMacroReplacement,
5141        conditional: bool,
5142    },
5143    Undef {
5144        name: String,
5145        conditional: bool,
5146    },
5147}
5148
5149/// Collect object-like field-list macro events in source order. Conditional
5150/// branches remain visible in the event stream: callers that cannot prove
5151/// branch selection can invalidate names conservatively instead of inventing
5152/// a replacement from one branch.
5153pub fn collect_cpp_object_macro_field_events<'tree>(
5154    root: Node<'tree>,
5155    source: &str,
5156) -> Vec<ObjectMacroFieldEvent> {
5157    let mut events = Vec::new();
5158    let mut stack = vec![root];
5159    while let Some(node) = stack.pop() {
5160        if node.kind() == "preproc_def"
5161            && let Some(name) = extract_macro_name(node, source)
5162        {
5163            let replacement = object_macro_replacement_of(node, source);
5164            events.push(ObjectMacroFieldEvent::Define {
5165                name,
5166                replacement,
5167                conditional: inside_preprocessor_conditional(node),
5168            });
5169        } else if is_cpp_undef_directive(node, source)
5170            && let Some(argument) = node.child_by_field_name("argument")
5171        {
5172            events.push(ObjectMacroFieldEvent::Undef {
5173                name: node_text(argument, source).trim().to_string(),
5174                conditional: inside_preprocessor_conditional(node),
5175            });
5176        }
5177        let mut cursor = node.walk();
5178        let children = node.named_children(&mut cursor).collect::<Vec<_>>();
5179        stack.extend(children.into_iter().rev());
5180    }
5181    events
5182}
5183
5184fn inside_preprocessor_conditional(node: Node<'_>) -> bool {
5185    let mut current = node.parent();
5186    while let Some(parent) = current {
5187        if is_cpp_preprocessor_conditional(parent) {
5188            return true;
5189        }
5190        current = parent.parent();
5191    }
5192    false
5193}
5194
5195fn is_cpp_undef_directive(node: Node<'_>, source: &str) -> bool {
5196    node.kind() == "preproc_call"
5197        && node
5198            .child_by_field_name("directive")
5199            .is_some_and(|directive| node_text(directive, source).trim() == "#undef")
5200}
5201
5202impl<'a, 'source> CppVisitor<'a, 'source> {
5203    fn apply_active_source_context(
5204        &self,
5205        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5206    ) {
5207        let Some(context) = self.active_source_context.as_ref() else {
5208            return;
5209        };
5210        fact.guard_requirements = fact
5211            .guard_requirements
5212            .as_ref()
5213            .and_then(crate::source_context::cpp_guard_set_to_runtime)
5214            .zip(context.guards.as_ref())
5215            .and_then(|(local, outer)| {
5216                crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5217            })
5218            .as_ref()
5219            .map(crate::source_context::cpp_guard_set_from_runtime);
5220        if fact.callable_guards.is_some() {
5221            fact.callable_guards = fact
5222                .callable_guards
5223                .as_ref()
5224                .and_then(crate::source_context::cpp_guard_set_to_runtime)
5225                .zip(context.guards.as_ref())
5226                .and_then(|(local, outer)| {
5227                    crate::graph::resolver::merge_preprocessor_guards(&local, outer)
5228                })
5229                .as_ref()
5230                .map(crate::source_context::cpp_guard_set_from_runtime);
5231        }
5232        fact.callable_guard_completion_byte = fact
5233            .callable_guard_completion_byte
5234            .zip(context.callable_guard_completion_byte)
5235            .map(|(local, outer)| local.max(outer));
5236        fact.conditional_family = fact.conditional_family.or(context.family);
5237        fact.exhaustive_conditional_family = fact
5238            .exhaustive_conditional_family
5239            .or(context.exhaustive_family);
5240        if let Some(alias) = fact.file_scope_alias.as_mut() {
5241            alias.namespace = match (context.namespace.as_ref(), alias.namespace.take()) {
5242                (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
5243                (Some(outer), None) => Some(outer.clone()),
5244                (None, local) => local,
5245            };
5246        }
5247        if !context.outer_lexical_scope.is_empty() {
5248            let mut lexical = context.outer_lexical_scope.clone();
5249            lexical.append(&mut fact.lexical_path);
5250            fact.lexical_path = lexical;
5251        }
5252    }
5253
5254    fn recovery_ancestry<'tree>(
5255        &self,
5256        root: Node<'tree>,
5257    ) -> crate::source_dependencies::RecoveryAncestry<'a, 'tree> {
5258        crate::source_dependencies::RecoveryAncestry::new(
5259            root,
5260            self.source,
5261            self.source_dependencies,
5262        )
5263    }
5264
5265    fn capture_source_dependencies(
5266        &self,
5267        fact: &mut brokk_bifrost_core::analyzer::cpp_facts::CppDeclarationSourceFact,
5268        node: Node<'_>,
5269        unit: &CodeUnit,
5270    ) {
5271        if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
5272            self.source_dependencies.borrow_mut().register_primary(
5273                fact.declaration,
5274                node,
5275                unit.is_class(),
5276            );
5277        } else {
5278            self.source_dependencies
5279                .borrow()
5280                .apply_recovered(node, unit.is_class(), fact);
5281        }
5282    }
5283
5284    fn link_source_declaration(
5285        &mut self,
5286        ancestry: &ParentIndex<'_>,
5287        unit: &CodeUnit,
5288        node: Node<'_>,
5289    ) {
5290        let (declaration, family) = {
5291            let mut collector = self.source_collector.borrow_mut();
5292            let occurrence = if node.id() == self.primary_root_id
5293                || self.primary_ancestry.contains(node)
5294            {
5295                collector.intern_node(node)
5296            } else {
5297                // A region reparse owns distinct provenance even when its byte range
5298                // equals a primary node or a previously dropped recovery tree.
5299                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
5300            };
5301            let name = crate::source_facts::declaration_name(node, unit, self.source).map(|name| {
5302                if self.primary_ancestry.contains(name) {
5303                    collector.intern_node(name)
5304                } else {
5305                    collector
5306                        .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(name))
5307                }
5308            });
5309            let declaration = collector.declare(occurrence, name);
5310            let family =
5311                crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5312                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5313                    {
5314                        collector.intern_node(family)
5315                    } else {
5316                        collector.intern_embedded(
5317                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5318                        )
5319                    }
5320                });
5321            (declaration, family)
5322        };
5323        self.source_declarations.insert(declaration);
5324        if !self.source_properties.borrow().contains_key(&declaration) {
5325            let context = crate::source_context::capture_declaration_context(
5326                node,
5327                unit,
5328                self.source,
5329                &mut self.preprocessor_context_cache,
5330            );
5331            let (exhaustive_family, displaced_namespace_closing_brace) = {
5332                let mut collector = self.source_collector.borrow_mut();
5333                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5334                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5335                    {
5336                        collector.intern_node(family)
5337                    } else {
5338                        collector.intern_embedded(
5339                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5340                        )
5341                    }
5342                });
5343                let displaced_namespace_closing_brace =
5344                    context.displaced_namespace_closing_brace.map(|brace| {
5345                        if brace.id() == self.primary_root_id
5346                            || self.primary_ancestry.contains(brace)
5347                        {
5348                            collector.intern_node(brace)
5349                        } else {
5350                            collector.intern_embedded(
5351                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5352                            )
5353                        }
5354                    });
5355                (exhaustive_family, displaced_namespace_closing_brace)
5356            };
5357            let guard_requirements = context.guard_requirements;
5358            let callable_guards = context.callable_guards;
5359            let callable_activation = context.callable_activation;
5360            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5361            let flattened_macro_namespace = context.flattened_macro_namespace;
5362            self.source_properties
5363                .borrow_mut()
5364                .entry(declaration)
5365                .or_insert_with(|| {
5366                    let mut fact = crate::source_facts::capture_declaration(
5367                        declaration,
5368                        node,
5369                        unit,
5370                        self.source,
5371                        ancestry,
5372                    );
5373                    self.capture_source_dependencies(&mut fact, node, unit);
5374                    fact.occurrence_role =
5375                        crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5376                    fact.conditional_family = family;
5377                    fact.guard_requirements = guard_requirements;
5378                    fact.callable_guards = callable_guards;
5379                    fact.callable_activation = callable_activation;
5380                    fact.callable_guard_completion_byte = callable_guard_completion_byte;
5381                    fact.exhaustive_conditional_family = exhaustive_family;
5382                    fact.flattened_macro_namespace = flattened_macro_namespace;
5383                    fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5384                    self.apply_active_source_context(&mut fact);
5385                    fact
5386                });
5387        }
5388        let links = self.source_links.entry(unit.clone()).or_default();
5389        if !links.contains(&declaration) {
5390            links.push(declaration);
5391        }
5392    }
5393
5394    fn link_source_range(
5395        &mut self,
5396        ancestry: &ParentIndex<'_>,
5397        unit: &CodeUnit,
5398        range: Range,
5399        evidence: Option<Node<'_>>,
5400    ) {
5401        let declaration = {
5402            let mut collector = self.source_collector.borrow_mut();
5403            let occurrence =
5404                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5405            let name = evidence
5406                .and_then(|node| crate::source_facts::declaration_name(node, unit, self.source))
5407                .map(|name| {
5408                    if self.primary_ancestry.contains(name) {
5409                        collector.intern_node(name)
5410                    } else {
5411                        collector.intern_embedded(
5412                            brokk_bifrost_core::analyzer::tree_walk::node_range(name),
5413                        )
5414                    }
5415                });
5416            collector.declare(occurrence, name)
5417        };
5418        self.link_source_declaration_facts(
5419            unit,
5420            declaration,
5421            evidence.map(|node| (ancestry, node)),
5422        );
5423    }
5424
5425    /// [`Self::link_source_range`] with the name occurrence interned from the
5426    /// subspan that writes it instead of derived from an evidence node. A live
5427    /// evidence node still supplies the declaration's source facts.
5428    fn link_source_name_range(
5429        &mut self,
5430        unit: &CodeUnit,
5431        range: Range,
5432        name_range: Range,
5433        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5434    ) {
5435        let declaration = {
5436            let mut collector = self.source_collector.borrow_mut();
5437            let occurrence =
5438                collector.intern_subspan(range, SourceOccurrenceProvenance::ExplicitSubspan);
5439            let name =
5440                collector.intern_subspan(name_range, SourceOccurrenceProvenance::ExplicitSubspan);
5441            collector.declare(occurrence, Some(name))
5442        };
5443        self.link_source_declaration_facts(unit, declaration, evidence);
5444    }
5445
5446    /// Record `declaration` for `unit` and, when a live evidence node exists,
5447    /// the source facts it supplies: conditional family, guards, context and
5448    /// dependency names. Shared by both occurrence-naming paths so a name
5449    /// subspan never costs a declaration its facts (#3093).
5450    fn link_source_declaration_facts(
5451        &mut self,
5452        unit: &CodeUnit,
5453        declaration: SourceDeclarationId,
5454        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5455    ) {
5456        let family = evidence.and_then(|(_, node)| {
5457            let mut collector = self.source_collector.borrow_mut();
5458            crate::graph::resolver::preprocessor_conditional_family_node(node).map(|family| {
5459                if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
5460                    collector.intern_node(family)
5461                } else {
5462                    collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(
5463                        family,
5464                    ))
5465                }
5466            })
5467        });
5468        self.source_declarations.insert(declaration);
5469        if let Some((ancestry, node)) = evidence
5470            && !self.source_properties.borrow().contains_key(&declaration)
5471        {
5472            let context = crate::source_context::capture_declaration_context(
5473                node,
5474                unit,
5475                self.source,
5476                &mut self.preprocessor_context_cache,
5477            );
5478            let (exhaustive_family, displaced_namespace_closing_brace) = {
5479                let mut collector = self.source_collector.borrow_mut();
5480                let exhaustive_family = context.exhaustive_conditional_family.map(|family| {
5481                    if family.id() == self.primary_root_id || self.primary_ancestry.contains(family)
5482                    {
5483                        collector.intern_node(family)
5484                    } else {
5485                        collector.intern_embedded(
5486                            brokk_bifrost_core::analyzer::tree_walk::node_range(family),
5487                        )
5488                    }
5489                });
5490                let displaced_namespace_closing_brace =
5491                    context.displaced_namespace_closing_brace.map(|brace| {
5492                        if brace.id() == self.primary_root_id
5493                            || self.primary_ancestry.contains(brace)
5494                        {
5495                            collector.intern_node(brace)
5496                        } else {
5497                            collector.intern_embedded(
5498                                brokk_bifrost_core::analyzer::tree_walk::node_range(brace),
5499                            )
5500                        }
5501                    });
5502                (exhaustive_family, displaced_namespace_closing_brace)
5503            };
5504            let guard_requirements = context.guard_requirements;
5505            let callable_guards = context.callable_guards;
5506            let callable_activation = context.callable_activation;
5507            let callable_guard_completion_byte = context.callable_guard_completion_byte;
5508            let flattened_macro_namespace = context.flattened_macro_namespace;
5509            let mut fact = crate::source_facts::capture_declaration(
5510                declaration,
5511                node,
5512                unit,
5513                self.source,
5514                ancestry,
5515            );
5516            self.capture_source_dependencies(&mut fact, node, unit);
5517            fact.occurrence_role =
5518                crate::identity::cpp_occurrence_role_for_node(node, self.source, unit);
5519            fact.conditional_family = family;
5520            fact.guard_requirements = guard_requirements;
5521            fact.callable_guards = callable_guards;
5522            fact.callable_activation = callable_activation;
5523            fact.callable_guard_completion_byte = callable_guard_completion_byte;
5524            fact.exhaustive_conditional_family = exhaustive_family;
5525            fact.flattened_macro_namespace = flattened_macro_namespace;
5526            fact.displaced_namespace_closing_brace = displaced_namespace_closing_brace;
5527            self.apply_active_source_context(&mut fact);
5528            self.source_properties
5529                .borrow_mut()
5530                .insert(declaration, fact);
5531        }
5532        let links = self.source_links.entry(unit.clone()).or_default();
5533        if !links.contains(&declaration) {
5534            links.push(declaration);
5535        }
5536    }
5537
5538    fn add_signature_with_metadata(&mut self, unit: CodeUnit, metadata: SignatureMetadata) {
5539        let declaration = *self
5540            .source_links
5541            .get(&unit)
5542            .and_then(|links| links.last())
5543            .expect("signature metadata follows its source declaration");
5544        let metadata_ordinal = self
5545            .parsed
5546            .add_signature_with_metadata(unit.clone(), metadata);
5547        let link = brokk_bifrost_core::analyzer::parsed_file::SourceDeclarationMetadataLink {
5548            declaration,
5549            unit,
5550            metadata_ordinal,
5551        };
5552        if !self.parsed.source_declaration_metadata.contains(&link) {
5553            self.parsed.source_declaration_metadata.push(link);
5554        }
5555    }
5556
5557    fn add_callable_signature_with_metadata(
5558        &mut self,
5559        unit: CodeUnit,
5560        declaration_start: usize,
5561        declarator: Node<'_>,
5562        ancestry: &ParentIndex<'_>,
5563        metadata: SignatureMetadata,
5564    ) {
5565        self.callable_observer.observe(
5566            &unit,
5567            declaration_start,
5568            &metadata,
5569            declarator,
5570            self.source,
5571            ancestry,
5572        );
5573        // A malformed default argument can leave no complete parameter type
5574        // to prove the overload discriminator. Keep the physical observation
5575        // above for partial evidence, but do not persist a signature row
5576        // without that proof.
5577        if metadata.overload_discriminator().is_some() {
5578            self.add_signature_with_metadata(unit, metadata);
5579        }
5580    }
5581
5582    /// Records `code_unit` with the answers the walk carries forward, then adds
5583    /// it to the parse product.
5584    ///
5585    /// Every declaration this walk publishes goes through this family, so the
5586    /// carried-forward answers see each one exactly once: the minted declaration
5587    /// set every open [`Self::record_recovered_declarations`] reports (#2787).
5588    fn add_declaration(
5589        &mut self,
5590        ancestry: &ParentIndex<'_>,
5591        code_unit: CodeUnit,
5592        node: Node<'_>,
5593        parent: Option<CodeUnit>,
5594        top_level: Option<CodeUnit>,
5595    ) {
5596        self.note_declaration(&code_unit);
5597        self.link_source_declaration(ancestry, &code_unit, node);
5598        let source = self.source;
5599        self.parsed
5600            .add_code_unit(code_unit, node, source, parent, top_level);
5601    }
5602
5603    /// Range-based form of [`Self::add_declaration`].
5604    fn add_declaration_with_range(
5605        &mut self,
5606        ancestry: &ParentIndex<'_>,
5607        code_unit: CodeUnit,
5608        range: Range,
5609        evidence: Option<Node<'_>>,
5610        parent: Option<CodeUnit>,
5611        top_level: Option<CodeUnit>,
5612    ) {
5613        self.note_declaration(&code_unit);
5614        self.link_source_range(ancestry, &code_unit, range, evidence);
5615        self.parsed
5616            .add_code_unit_with_range(code_unit, range, parent, top_level);
5617    }
5618
5619    /// Name-subspan form of [`Self::add_declaration_with_range`]: the name
5620    /// occurrence is the recorded subspan rather than a token derived from an
5621    /// evidence node (#4292). A live `evidence` node, when the caller has one,
5622    /// still supplies the declaration's source facts; an index-recovered
5623    /// declaration has none.
5624    fn add_declaration_with_name_range(
5625        &mut self,
5626        code_unit: CodeUnit,
5627        range: Range,
5628        name_range: Range,
5629        evidence: Option<(&ParentIndex<'_>, Node<'_>)>,
5630        parent: Option<CodeUnit>,
5631        top_level: Option<CodeUnit>,
5632    ) {
5633        self.note_declaration(&code_unit);
5634        self.link_source_name_range(&code_unit, range, name_range, evidence);
5635        self.parsed
5636            .add_code_unit_with_range(code_unit, range, parent, top_level);
5637    }
5638
5639    /// Deferred-replacement form of [`Self::add_declaration`].
5640    fn replace_declaration_deferred(
5641        &mut self,
5642        ancestry: &ParentIndex<'_>,
5643        code_unit: CodeUnit,
5644        node: Node<'_>,
5645        parent: Option<CodeUnit>,
5646        top_level: Option<CodeUnit>,
5647    ) {
5648        self.note_replaced_declaration(&code_unit);
5649        self.link_source_declaration(ancestry, &code_unit, node);
5650        let source = self.source;
5651        self.parsed
5652            .replace_code_unit_deferred(code_unit, node, source, parent, top_level);
5653    }
5654
5655    /// Range-based form of [`Self::replace_declaration_deferred`].
5656    fn replace_declaration_with_range_deferred(
5657        &mut self,
5658        ancestry: &ParentIndex<'_>,
5659        code_unit: CodeUnit,
5660        range: Range,
5661        evidence: Option<Node<'_>>,
5662        parent: Option<CodeUnit>,
5663        top_level: Option<CodeUnit>,
5664    ) {
5665        self.note_replaced_declaration(&code_unit);
5666        self.link_source_range(ancestry, &code_unit, range, evidence);
5667        self.parsed
5668            .replace_code_unit_with_range_deferred(code_unit, range, parent, top_level);
5669    }
5670
5671    /// Notes one declaration about to enter the parse product.
5672    ///
5673    /// A declaration the product already holds is not a creation, so an open
5674    /// recovery capture ignores it -- which is the membership test the set
5675    /// difference it replaces performed. A creation inside a nested recovery
5676    /// belongs to the recoveries around it too, so every open capture takes it.
5677    fn note_declaration(&mut self, code_unit: &CodeUnit) {
5678        if !self.recovery_captures.is_empty() && !self.parsed.contains_declaration(code_unit) {
5679            for capture in &mut self.recovery_captures {
5680                if capture.removed_pre_existing.contains(code_unit) {
5681                    continue;
5682                }
5683                if capture.created_units.insert(code_unit.clone()) {
5684                    capture.created.push(code_unit.clone());
5685                }
5686            }
5687        }
5688    }
5689
5690    /// Notes one declaration about to replace an existing one.
5691    ///
5692    /// A deferred replacement of a declaration that already owns children
5693    /// removes those children (`ParsedFile::prepare_deferred_replacement`), and
5694    /// their source mounts and recovery captures must track the removal.
5695    fn note_replaced_declaration(&mut self, code_unit: &CodeUnit) {
5696        // Forward and definition occurrences remain real navigation alternatives.
5697        // Only identities removed with replaced children lose their mounts.
5698        for removed in self.declarations_a_replacement_removes(code_unit) {
5699            self.source_links.remove(&removed);
5700        }
5701        let removes_children = self.parsed.contains_declaration(code_unit)
5702            && self
5703                .parsed
5704                .children
5705                .get(code_unit)
5706                .is_some_and(|children| !children.is_empty());
5707        if removes_children && !self.recovery_captures.is_empty() {
5708            let removed = self.declarations_a_replacement_removes(code_unit);
5709            for capture in &mut self.recovery_captures {
5710                for unit in &removed {
5711                    // A declaration this capture watched being created is
5712                    // its own; one it did not is a declaration that was
5713                    // already there when the capture opened, so creating it
5714                    // again is a restoration and not a mint.
5715                    if !capture.created_units.contains(unit) {
5716                        capture.removed_pre_existing.insert(unit.clone());
5717                    }
5718                }
5719            }
5720        }
5721        self.note_declaration(code_unit);
5722    }
5723
5724    /// The declarations `ParsedFile::prepare_deferred_replacement` will remove
5725    /// when `code_unit` is replaced: its children, transitively.
5726    fn declarations_a_replacement_removes(&self, code_unit: &CodeUnit) -> Vec<CodeUnit> {
5727        let mut removed = Vec::new();
5728        let mut seen = HashSet::default();
5729        let mut pending: Vec<CodeUnit> = self
5730            .parsed
5731            .children
5732            .get(code_unit)
5733            .cloned()
5734            .unwrap_or_default();
5735        while let Some(unit) = pending.pop() {
5736            if !seen.insert(unit.clone()) {
5737                continue;
5738            }
5739            if let Some(children) = self.parsed.children.get(&unit) {
5740                pending.extend(children.iter().cloned());
5741            }
5742            removed.push(unit);
5743        }
5744        removed
5745    }
5746
5747    fn visit_recovered_function_like_export_class<'tree>(
5748        &mut self,
5749        node: Node<'tree>,
5750        recovered: RecoveredFunctionLikeExportClass,
5751        scope: &ScopeInfo,
5752        stack: &mut Vec<CppWork<'tree>>,
5753        ancestry: &ParentIndex<'tree>,
5754    ) {
5755        let RecoveredFunctionLikeExportClass {
5756            name,
5757            range,
5758            raw_supertypes,
5759            base_facts,
5760            member_outcome,
5761        } = recovered;
5762        let class_unit = self.visit_named_class_like_shape(
5763            node,
5764            name,
5765            None,
5766            true,
5767            Some(range),
5768            raw_supertypes,
5769            base_facts,
5770            scope,
5771            stack,
5772            ancestry,
5773        );
5774        self.parsed
5775            .record_materialization(MaterializationRecord::RecoveredDeclaration {
5776                recovery: range,
5777                unit: class_unit.clone(),
5778            });
5779        if let Some(FragmentedExportMembers::Complete(tree)) = member_outcome.as_ref()
5780            && let Some((constructor_range, body)) = cpp_reparsed_merged_inline_constructor(
5781                tree.root_node(),
5782                class_unit.identifier(),
5783                self.source,
5784            )
5785        {
5786            self.visit_recovered_fragment_constructor(
5787                constructor_range,
5788                body,
5789                node,
5790                &class_unit,
5791                scope,
5792                ancestry,
5793            );
5794        }
5795        if let Some(outcome) = member_outcome {
5796            self.visit_fragmented_export_class_members(node, outcome, class_unit, scope);
5797        }
5798    }
5799
5800    fn visit_function_like_export_class_pair<'tree>(
5801        &mut self,
5802        node: Node<'tree>,
5803        scope: &ScopeInfo,
5804        stack: &mut Vec<CppWork<'tree>>,
5805        ancestry: &ParentIndex<'tree>,
5806    ) -> bool {
5807        let Some(recovered) = recover_function_like_export_class_pair(node, self.source) else {
5808            return false;
5809        };
5810        let member_outcome = self
5811            .reparse_fragmented_export_class_members(&recovered.fragmented_body, &recovered.name);
5812        // A malformed class body can escape into several following siblings
5813        // before the next export-macro class head appears. Inspect siblings in
5814        // order and stop at the first envelope that contains such a head. One
5815        // envelope can contain several following classes, all recovered in a
5816        // single bounded traversal.
5817        let mut displaced = node.next_named_sibling();
5818        while let Some(candidate) = displaced {
5819            if self.visit_embedded_function_like_export_classes(candidate, scope, stack, ancestry) {
5820                break;
5821            }
5822            displaced = candidate.next_named_sibling();
5823        }
5824        // The adjacent initializer_list proves the class body envelope, but
5825        // its children are expression-shaped rather than declaration-shaped.
5826        // Visit only separately recovered members; ordinary callable
5827        // definitions remain available from their out-of-line declarations.
5828        let range = recovered.range;
5829        self.visit_recovered_function_like_export_class(
5830            node,
5831            RecoveredFunctionLikeExportClass {
5832                name: recovered.name,
5833                range,
5834                raw_supertypes: recovered.raw_supertypes,
5835                base_facts: recovered.base_facts,
5836                member_outcome,
5837            },
5838            scope,
5839            stack,
5840            ancestry,
5841        );
5842        self.consumed_fragment_regions
5843            .push((node.start_byte(), range.end_byte));
5844        true
5845    }
5846
5847    fn visit_embedded_function_like_export_classes<'tree>(
5848        &mut self,
5849        node: Node<'tree>,
5850        scope: &ScopeInfo,
5851        stack: &mut Vec<CppWork<'tree>>,
5852        ancestry: &ParentIndex<'tree>,
5853    ) -> bool {
5854        let recovered_classes = recover_embedded_function_like_export_classes(node, self.source);
5855        let found = !recovered_classes.is_empty();
5856        for recovered in recovered_classes {
5857            let member_outcome = self.reparse_fragmented_export_class_members(
5858                &recovered.fragmented_body,
5859                &recovered.name,
5860            );
5861            self.visit_recovered_function_like_export_class(
5862                node,
5863                RecoveredFunctionLikeExportClass {
5864                    name: recovered.name,
5865                    range: recovered.range,
5866                    raw_supertypes: Some(recovered.raw_supertypes),
5867                    base_facts: Some(recovered.base_facts),
5868                    member_outcome,
5869                },
5870                scope,
5871                stack,
5872                ancestry,
5873            );
5874        }
5875        found
5876    }
5877
5878    /// Walk `node`'s container, answering every ancestor question from
5879    /// `ancestry`.
5880    ///
5881    /// `ancestry` must index the tree `node` belongs to. The caller owns it
5882    /// because one tree can be walked more than once -- a header's C and C++
5883    /// readings are the same tree under different tag semantics -- and the
5884    /// parent relation is a property of the tree, not of the reading.
5885    #[allow(clippy::too_many_arguments)]
5886    pub fn visit_container<'tree>(
5887        &mut self,
5888        node: Node<'tree>,
5889        ancestry: &ParentIndex<'tree>,
5890        package_name: &str,
5891        module: Option<CodeUnit>,
5892        class_unit: Option<CodeUnit>,
5893        template_signature: Option<String>,
5894        visible_using_namespaces: Vec<String>,
5895        visible_using_declarations: Arc<Vec<CppUsingDeclarationBinding>>,
5896    ) {
5897        let scope = ScopeInfo {
5898            package_name: package_name.to_string(),
5899            module,
5900            class_unit,
5901            template_signature,
5902            template_metadata: None,
5903            declarations_are_fields: false,
5904            recovered_specialization_member_scope: false,
5905            visible_using_namespaces,
5906            local_block: None,
5907            visible_using_declarations,
5908        };
5909        // The work loop dispatches a container's children, never the container
5910        // itself, so an `ERROR` container that is the seed does not reach the
5911        // ERROR arm of `visit_node`. A header whose first collapsed aggregate
5912        // breaks the parse leaves the whole translation unit as that seed
5913        // (libuv's `include/uv.h`, issue #2985), which is exactly the shape the
5914        // macro-error-class recovery reads.
5915        if node.is_error() {
5916            self.visit_object_macro_error_classes(node, &scope);
5917        }
5918        self.run_container_work(node, scope, ancestry);
5919        self.drain_partitioned_regions();
5920    }
5921
5922    /// Drain reparsed containers after their primary walk has indexed them.
5923    /// Recovered nodes retain their own tree and source context while they are
5924    /// visited; only then are their byte ranges marked consumed so a shared
5925    /// primary event stream does not visit the same declarations twice.
5926    fn drain_partitioned_regions(&mut self) {
5927        let mut consumed = Vec::new();
5928        while let Some(CppPartitionedRegion {
5929            tree,
5930            range,
5931            scope,
5932            recovery,
5933            source_context,
5934        }) = self.partitioned_regions.pop()
5935        {
5936            let root = tree.root_node();
5937            let container = root
5938                .descendant_for_byte_range(range.start, range.end)
5939                .expect("the queued container belongs to this tree");
5940            assert_eq!(container.byte_range(), range);
5941            let ancestry = self.recovery_ancestry(root);
5942            self.record_recovered_declarations_with_context(recovery, source_context, |visitor| {
5943                visitor.run_container_work(container, scope, &ancestry)
5944            });
5945            // A recovered walk may enqueue another partition inside this
5946            // region. Delay the outer claim until all nested regions have
5947            // drained, or their nodes would be skipped as already consumed.
5948            consumed.push((range.start, range.end));
5949        }
5950        self.consumed_fragment_regions.extend(consumed);
5951    }
5952
5953    /// Whether a work node lies entirely inside a byte region consumed by a
5954    /// fragmented export-class recovery (#938); such nodes were already indexed
5955    /// as members of the recovered class by the region reparse.
5956    fn node_is_inside_consumed_fragment(&self, node: Node<'_>) -> bool {
5957        self.byte_range_is_inside_consumed_fragment(node.start_byte(), node.end_byte())
5958    }
5959
5960    /// Byte-range form of [`Self::node_is_inside_consumed_fragment`], for a
5961    /// candidate a recovery is about to reparse but has not yet turned into a
5962    /// node -- e.g. a prototype-macro candidate (#2932) already claimed by a
5963    /// prior structured recovery.
5964    fn byte_range_is_inside_consumed_fragment(&self, start: usize, end: usize) -> bool {
5965        self.consumed_fragment_regions
5966            .iter()
5967            .any(|&(region_start, region_end)| start >= region_start && end <= region_end)
5968    }
5969
5970    /// Drive the container work loop from an explicit seed scope to completion. The
5971    /// loop is self-contained so a locally-owned reparsed tree (issue #938/#941)
5972    /// stays alive for the whole traversal.
5973    ///
5974    /// Every ancestor question this walk asks is answered from `ancestry`, which
5975    /// must index the tree `node` belongs to. Asking tree-sitter itself costs the
5976    /// node's position in the tree, which made a generated header with thousands
5977    /// of top-level declarations quadratic (#2361). The index is the caller's
5978    /// because it outlives any one walk: the file's tree is walked twice when a
5979    /// header has both a C and a C++ reading, and a region reparse (#938/#941)
5980    /// builds its own index for its own tree.
5981    fn run_container_work<'tree>(
5982        &mut self,
5983        node: Node<'tree>,
5984        scope: ScopeInfo,
5985        ancestry: &ParentIndex<'tree>,
5986    ) {
5987        self.drain_cpp_work(
5988            vec![CppWork::Container(CppContainer { node, scope })],
5989            ancestry,
5990        );
5991    }
5992
5993    /// The work loop itself, from whatever seed the caller built.
5994    ///
5995    /// [`Self::run_container_work`] seeds it with a whole container. A recovery
5996    /// that must walk only part of a reparsed tree seeds it with the sibling
5997    /// range it may walk instead, which keeps the `using namespace X;` scope
5998    /// accumulation `advance_cpp_siblings` performs.
5999    fn drain_cpp_work<'tree>(
6000        &mut self,
6001        mut stack: Vec<CppWork<'tree>>,
6002        ancestry: &ParentIndex<'tree>,
6003    ) {
6004        while let Some(work) = stack.pop() {
6005            match work {
6006                CppWork::Container(container) => {
6007                    push_cpp_container_work(container.node, container.scope, &mut stack);
6008                }
6009                CppWork::Siblings(siblings) => {
6010                    advance_cpp_siblings(siblings, self.source, &mut stack);
6011                }
6012                CppWork::Node(work) => {
6013                    if self.node_is_inside_consumed_fragment(work.node) {
6014                        continue;
6015                    }
6016                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
6017                }
6018            }
6019        }
6020    }
6021
6022    /// Drain nodes selected by a bounded recovery walk without filtering them
6023    /// against consumed regions from the primary tree.
6024    fn drain_cpp_recovery_work<'tree>(
6025        &mut self,
6026        mut stack: Vec<CppWork<'tree>>,
6027        ancestry: &ParentIndex<'tree>,
6028    ) {
6029        while let Some(work) = stack.pop() {
6030            match work {
6031                CppWork::Container(container) => {
6032                    push_cpp_container_work(container.node, container.scope, &mut stack);
6033                }
6034                CppWork::Siblings(siblings) => {
6035                    advance_cpp_siblings(siblings, self.source, &mut stack);
6036                }
6037                CppWork::Node(work) => {
6038                    self.visit_node(work.node, &work.scope, &mut stack, ancestry);
6039                }
6040            }
6041        }
6042    }
6043
6044    /// Reparse a fragmented multiple-base export class body (issue #938), admitting
6045    /// it only when the entire region is member-shaped. This validation must happen
6046    /// before registering the recovered class because a rejected speculative range
6047    /// must not leak into the ordinary recovery path.
6048    fn reparse_fragmented_export_class_members(
6049        &self,
6050        fragmented: &FragmentedExportBody,
6051        class_name: &str,
6052    ) -> Option<FragmentedExportMembers> {
6053        if fragmented.reparse_start >= fragmented.reparse_end {
6054            return None;
6055        }
6056        let tree = cpp_reparse_fragmented_class_body(
6057            self.source,
6058            fragmented.reparse_start,
6059            fragmented.reparse_end,
6060        )?;
6061        if cpp_reparsed_members_are_indexable_for_class(
6062            tree.root_node(),
6063            self.source,
6064            Some(class_name),
6065        ) {
6066            return Some(FragmentedExportMembers::Complete(tree));
6067        }
6068        let has_conditional_constructor = {
6069            let root = tree.root_node();
6070            let mut cursor = root.walk();
6071            root.named_children(&mut cursor).any(|child| {
6072                cpp_reparsed_preprocessor_constructor(child, class_name, self.source).is_some()
6073            })
6074        };
6075        has_conditional_constructor.then_some(FragmentedExportMembers::ConditionalConstructor(tree))
6076    }
6077
6078    /// Index an already validated fragmented body as members of `class_unit`. The
6079    /// region reparse keeps each member's exact original byte and line positions.
6080    fn visit_fragmented_export_class_members(
6081        &mut self,
6082        origin: Node<'_>,
6083        outcome: FragmentedExportMembers,
6084        class_unit: CodeUnit,
6085        scope: &ScopeInfo,
6086    ) -> bool {
6087        self.with_recovery_source_context(origin, |visitor| {
6088            visitor.visit_fragmented_export_class_members_in_context(outcome, class_unit, scope)
6089        })
6090    }
6091
6092    fn visit_fragmented_export_class_members_in_context(
6093        &mut self,
6094        outcome: FragmentedExportMembers,
6095        class_unit: CodeUnit,
6096        scope: &ScopeInfo,
6097    ) -> bool {
6098        let (tree, complete) = match outcome {
6099            FragmentedExportMembers::Complete(tree) => (tree, true),
6100            FragmentedExportMembers::ConditionalConstructor(tree) => (tree, false),
6101        };
6102        let root = tree.root_node();
6103        let class_name = class_unit.identifier().to_string();
6104        let member_scope = ScopeInfo {
6105            // A recovered export-macro class may borrow its namespace from an
6106            // earlier forward declaration even when the malformed node itself
6107            // sits at file scope. Use the recovered class identity as the
6108            // authoritative package for reparsed members as well.
6109            package_name: class_unit.package_name().to_string(),
6110            module: scope.module.clone(),
6111            class_unit: Some(class_unit),
6112            template_signature: scope.template_signature.clone(),
6113            template_metadata: None,
6114            declarations_are_fields: true,
6115            recovered_specialization_member_scope: false,
6116            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6117            local_block: None,
6118            visible_using_declarations: scope.visible_using_declarations.clone(),
6119        };
6120        let reparsed_ancestry = self.recovery_ancestry(root);
6121        if !complete {
6122            // A conditional beginning immediately after an access label can
6123            // fragment one constructor declaration while leaving the rest of
6124            // the class body as unsafe statement soup. Recover only that
6125            // structurally proven constructor and leave the outer-tree
6126            // siblings unconsumed for their ordinary walk.
6127            let mut cursor = root.walk();
6128            let constructors = root
6129                .named_children(&mut cursor)
6130                .filter_map(|child| {
6131                    cpp_reparsed_preprocessor_constructor(child, &class_name, self.source)
6132                })
6133                .collect::<Vec<_>>();
6134            // The reparsed region is its own tree, so this drain walks it with
6135            // its own parent index.
6136            for constructor in constructors {
6137                let mut stack = Vec::new();
6138                self.visit_node(constructor, &member_scope, &mut stack, &reparsed_ancestry);
6139                self.drain_cpp_recovery_work(stack, &reparsed_ancestry);
6140            }
6141            return false;
6142        }
6143        let mut cursor = root.walk();
6144        let members = root.named_children(&mut cursor).collect::<Vec<_>>();
6145        for member in members.iter().copied().filter(|member| {
6146            cpp_reparsed_defaulted_constructor_is_indexable(
6147                *member,
6148                &members,
6149                Some(class_name.as_str()),
6150                self.source,
6151            )
6152        }) {
6153            self.visit_recovered_defaulted_constructor(member, &member_scope, &reparsed_ancestry);
6154        }
6155        for member in members.iter().copied().filter(|member| {
6156            cpp_reparsed_bare_constructor_call(*member, class_name.as_str(), self.source).is_some()
6157        }) {
6158            self.visit_bare_constructor_expression(member, &member_scope, &reparsed_ancestry);
6159        }
6160        // The reparsed region is its own tree, so this walk indexes it itself.
6161        self.run_container_work(root, member_scope, &reparsed_ancestry);
6162        true
6163    }
6164
6165    fn visit_recovered_fragment_constructor<'tree>(
6166        &mut self,
6167        range: std::ops::Range<usize>,
6168        constructor_body: Node<'tree>,
6169        class_declaration: Node<'tree>,
6170        class_unit: &CodeUnit,
6171        scope: &ScopeInfo,
6172        ancestry: &ParentIndex<'tree>,
6173    ) {
6174        self.with_recovery_source_context(class_declaration, |visitor| {
6175            visitor.visit_recovered_fragment_constructor_in_context(
6176                range,
6177                constructor_body,
6178                class_declaration,
6179                class_unit,
6180                scope,
6181                ancestry,
6182            );
6183        });
6184    }
6185
6186    fn visit_recovered_fragment_constructor_in_context<'tree>(
6187        &mut self,
6188        range: std::ops::Range<usize>,
6189        constructor_body: Node<'tree>,
6190        class_declaration: Node<'tree>,
6191        class_unit: &CodeUnit,
6192        scope: &ScopeInfo,
6193        ancestry: &ParentIndex<'tree>,
6194    ) {
6195        let Some(tree) = cpp_reparse_region_items(self.source, range.start, range.end) else {
6196            return;
6197        };
6198        let Some(function_declarator) = cpp_reparsed_exact_constructor_declarator(
6199            tree.root_node(),
6200            range.start,
6201            class_unit.identifier(),
6202            self.source,
6203        ) else {
6204            return;
6205        };
6206        let member_scope = ScopeInfo {
6207            package_name: class_unit.package_name().to_string(),
6208            module: scope.module.clone(),
6209            class_unit: Some(class_unit.clone()),
6210            template_signature: scope.template_signature.clone(),
6211            template_metadata: None,
6212            declarations_are_fields: true,
6213            recovered_specialization_member_scope: false,
6214            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6215            local_block: None,
6216            visible_using_declarations: scope.visible_using_declarations.clone(),
6217        };
6218        let Some(function) = extract_function_info(function_declarator, self.source, &member_scope)
6219        else {
6220            return;
6221        };
6222        debug_assert_eq!(function.name, class_unit.identifier());
6223        let code_unit = function.code_unit(self.file.clone());
6224        let recovered_ancestry = self.recovery_ancestry(tree.root_node());
6225        self.add_declaration_with_range(
6226            &recovered_ancestry,
6227            code_unit.clone(),
6228            Range {
6229                start_byte: function_declarator.start_byte(),
6230                end_byte: constructor_body.end_byte(),
6231                start_line: function_declarator.start_position().row + 1,
6232                end_line: constructor_body.end_position().row + 1,
6233            },
6234            Some(function_declarator),
6235            None,
6236            None,
6237        );
6238        let declaration = *self.source_links[&code_unit]
6239            .last()
6240            .expect("recovered constructor source");
6241        {
6242            let mut properties = self.source_properties.borrow_mut();
6243            let fact = properties
6244                .get_mut(&declaration)
6245                .expect("recovered constructor source fact");
6246            fact.occurrence_role =
6247                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
6248            fact.callable_is_constructor = true;
6249        }
6250        self.add_callable_signature_with_metadata(
6251            code_unit.clone(),
6252            function_declarator.start_byte(),
6253            function_declarator,
6254            &recovered_ancestry,
6255            cpp_signature_metadata(
6256                normalize_cpp_whitespace(node_text(function_declarator, self.source)),
6257                function_declarator,
6258                cpp_callable_linkage(class_declaration, self.source, ancestry),
6259                self.source,
6260                &recovered_ancestry,
6261            )
6262            .with_declaration_only(false),
6263        );
6264        self.parsed.add_child(class_unit.clone(), code_unit);
6265    }
6266
6267    fn visit_recovered_fragment_prefix_members<'tree>(
6268        &mut self,
6269        origin: Node<'_>,
6270        root: Node<'tree>,
6271        constructor_start: usize,
6272        class_unit: &CodeUnit,
6273        scope: &ScopeInfo,
6274        ancestry: &ParentIndex<'tree>,
6275    ) {
6276        self.with_recovery_source_context(origin, |visitor| {
6277            visitor.visit_recovered_fragment_prefix_members_in_context(
6278                root,
6279                constructor_start,
6280                class_unit,
6281                scope,
6282                ancestry,
6283            );
6284        });
6285    }
6286
6287    fn visit_recovered_fragment_prefix_members_in_context<'tree>(
6288        &mut self,
6289        root: Node<'tree>,
6290        constructor_start: usize,
6291        class_unit: &CodeUnit,
6292        scope: &ScopeInfo,
6293        ancestry: &ParentIndex<'tree>,
6294    ) {
6295        let member_scope = ScopeInfo {
6296            package_name: class_unit.package_name().to_string(),
6297            module: scope.module.clone(),
6298            class_unit: Some(class_unit.clone()),
6299            template_signature: scope.template_signature.clone(),
6300            template_metadata: None,
6301            declarations_are_fields: true,
6302            recovered_specialization_member_scope: false,
6303            visible_using_namespaces: scope.visible_using_namespaces.clone(),
6304            local_block: None,
6305            visible_using_declarations: scope.visible_using_declarations.clone(),
6306        };
6307        let mut root_cursor = root.walk();
6308        let members = root.named_children(&mut root_cursor).collect::<Vec<_>>();
6309        let mut stack = vec![root];
6310        while let Some(current) = stack.pop() {
6311            if current.kind() == "comment" || current.start_byte() >= constructor_start {
6312                continue;
6313            }
6314            if current
6315                .parent()
6316                .is_some_and(|parent| same_node(parent, root))
6317                && cpp_reparsed_defaulted_constructor_is_indexable(
6318                    current,
6319                    &members,
6320                    Some(class_unit.identifier()),
6321                    self.source,
6322                )
6323            {
6324                self.visit_recovered_defaulted_constructor(current, &member_scope, ancestry);
6325                continue;
6326            }
6327            if current.end_byte() <= constructor_start
6328                && current.kind() != "translation_unit"
6329                && current.kind() != "labeled_statement"
6330                && current.kind() != "ERROR"
6331            {
6332                let mut work_stack = Vec::new();
6333                self.visit_node(current, &member_scope, &mut work_stack, ancestry);
6334                self.drain_cpp_recovery_work(work_stack, ancestry);
6335                continue;
6336            }
6337            if matches!(
6338                current.kind(),
6339                "translation_unit" | "labeled_statement" | "ERROR"
6340            ) {
6341                let mut cursor = current.walk();
6342                stack.extend(current.named_children(&mut cursor));
6343            }
6344        }
6345    }
6346
6347    fn visit_recovered_defaulted_constructor(
6348        &mut self,
6349        statement: Node<'_>,
6350        scope: &ScopeInfo,
6351        ancestry: &ParentIndex<'_>,
6352    ) {
6353        let Some(parent) = scope.class_unit.clone() else {
6354            return;
6355        };
6356        let Some(assignment) = statement.named_child(0) else {
6357            return;
6358        };
6359        let Some(call) = assignment.child_by_field_name("left") else {
6360            return;
6361        };
6362        let Some(function_name) = call.child_by_field_name("function") else {
6363            return;
6364        };
6365        let Some(arguments) = call.child_by_field_name("arguments") else {
6366            return;
6367        };
6368        if function_name.kind() != "identifier"
6369            || node_text(function_name, self.source) != parent.identifier()
6370            || arguments.kind() != "argument_list"
6371            || arguments.named_child_count() != 0
6372        {
6373            return;
6374        }
6375        let function = FunctionInfo {
6376            package_name: scope.package_name.clone(),
6377            owner: Some(CppMemberOwner::Unit(parent.clone())),
6378            name: parent.identifier().to_string(),
6379            signature: normalize_cpp_whitespace(node_text(arguments, self.source)),
6380        };
6381        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6382        if self.parsed.contains_declaration(&code_unit) {
6383            return;
6384        }
6385        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6386        let metadata = SignatureMetadata::new(
6387            normalize_cpp_whitespace(node_text(statement, self.source)),
6388            Vec::new(),
6389        )
6390        .with_declaration_only(true)
6391        .with_callable_arity(CallableArity::exact(0))
6392        .with_overload_discriminator(Some(
6393            brokk_bifrost_core::analyzer::tree_walk::overload_discriminator(Vec::<String>::new()),
6394        ))
6395        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6396        // The reparse proves class ownership, but carries no original class
6397        // access ladder. Preserve unknown access rather than guessing it.
6398        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6399        self.add_signature_with_metadata(code_unit.clone(), metadata);
6400        if let Some(declaration) = self
6401            .source_links
6402            .get(&code_unit)
6403            .and_then(|links| links.last())
6404            .copied()
6405        {
6406            let mut properties = self.source_properties.borrow_mut();
6407            let fact = properties
6408                .get_mut(&declaration)
6409                .expect("recovered defaulted constructor source fact");
6410            fact.occurrence_role =
6411                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6412            fact.callable_is_constructor = true;
6413        }
6414        self.parsed.add_child(parent, code_unit);
6415    }
6416
6417    /// Recover a constructor declaration a statement-context class-body
6418    /// reparse read as a call or parenthesized variable declarator. The
6419    /// statement is validated and its parameters read from a synthetic class
6420    /// context, where the constructor declaration parses as a real function
6421    /// declarator; the declaration itself is minted from the original
6422    /// statement node, so every range keeps its original byte positions.
6423    fn visit_bare_constructor_expression<'tree>(
6424        &mut self,
6425        statement: Node<'tree>,
6426        scope: &ScopeInfo,
6427        ancestry: &ParentIndex<'tree>,
6428    ) -> bool {
6429        let Some(parent) = scope.class_unit.clone() else {
6430            return false;
6431        };
6432        const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
6433        let region = &self.source[statement.start_byte()..statement.end_byte()];
6434        let parsed = crate::graph::syntax::parse_sentinel(
6435            PREFIX,
6436            crate::graph::syntax::SentinelInput::region(region),
6437            " };",
6438        );
6439        let (synthetic, tree) = (parsed.source, parsed.tree);
6440        let root = tree.root_node();
6441        if root.has_error() {
6442            return false;
6443        }
6444        let mut declarator = None;
6445        let mut stack = vec![root];
6446        while let Some(current) = stack.pop() {
6447            if current.kind() == "function_declarator" {
6448                if declarator.is_some() {
6449                    return false;
6450                }
6451                declarator = Some(current);
6452                continue;
6453            }
6454            let mut cursor = current.walk();
6455            stack.extend(current.named_children(&mut cursor));
6456        }
6457        let Some(declarator) = declarator else {
6458            return false;
6459        };
6460        let Some(parameters_node) = declarator.child_by_field_name("parameters") else {
6461            return false;
6462        };
6463        let Ok(callable_slots) = cpp_callable_parameter_slots(parameters_node, &synthetic) else {
6464            return false;
6465        };
6466        let callable_arity = cpp_callable_arity_from_slots(&callable_slots);
6467        let signature = cpp_parameter_signature(parameters_node, &synthetic);
6468        let mut parameter_labels = Vec::new();
6469        let mut parameter_cursor = parameters_node.walk();
6470        for parameter in parameters_node.named_children(&mut parameter_cursor) {
6471            if matches!(
6472                parameter.kind(),
6473                "parameter_declaration" | "optional_parameter_declaration"
6474            ) && let Some(name) = parameter
6475                .child_by_field_name("declarator")
6476                .and_then(|declarator| extract_variable_name(declarator, &synthetic))
6477            {
6478                parameter_labels.push(name);
6479            }
6480        }
6481        let function = FunctionInfo {
6482            package_name: scope.package_name.clone(),
6483            owner: Some(CppMemberOwner::Unit(parent.clone())),
6484            name: parent.identifier().to_string(),
6485            signature,
6486        };
6487        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
6488        if self.parsed.contains_declaration(&code_unit) {
6489            return true;
6490        }
6491        self.add_declaration(ancestry, code_unit.clone(), statement, None, None);
6492        let metadata = SignatureMetadata::with_parameter_labels(
6493            normalize_cpp_whitespace(node_text(statement, self.source)),
6494            parameter_labels,
6495        )
6496        .with_declaration_only(true)
6497        .with_callable_arity(callable_arity)
6498        .with_overload_discriminator(cpp_overload_discriminator(declarator, &synthetic))
6499        .with_callable_linkage(cpp_callable_linkage(statement, self.source, ancestry))
6500        // The reparse proves class ownership, but carries no original
6501        // class access ladder. Preserve unknown access rather than
6502        // guessing it.
6503        .with_callable_modifiers(false, true, DeclaredVisibility::Unknown);
6504        self.add_signature_with_metadata(code_unit.clone(), metadata);
6505        if let Some(declaration) = self
6506            .source_links
6507            .get(&code_unit)
6508            .and_then(|links| links.last())
6509            .copied()
6510        {
6511            let mut properties = self.source_properties.borrow_mut();
6512            let fact = properties
6513                .get_mut(&declaration)
6514                .expect("recovered constructor source fact");
6515            fact.occurrence_role =
6516                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly;
6517            fact.callable_is_constructor = true;
6518        }
6519        self.parsed.add_child(parent, code_unit);
6520        true
6521    }
6522
6523    /// Capture ancestors only when a proven recovery enters another tree. The
6524    /// ordinary declaration driver never performs this additional ancestor walk.
6525    fn with_recovery_source_context<T>(
6526        &mut self,
6527        node: Node<'_>,
6528        walk: impl FnOnce(&mut Self) -> T,
6529    ) -> T {
6530        self.with_recovery_source_context_at(node, node, walk)
6531    }
6532
6533    /// [`Self::with_recovery_source_context`] for a recovery whose content
6534    /// begins inside `node` at `preprocessor_anchor`: the lexical owners come
6535    /// from `node`, the preprocessor facts from the anchor. The two differ when
6536    /// the malformed node swallowed a branch directive that precedes the
6537    /// recovered content.
6538    fn with_recovery_source_context_at<T>(
6539        &mut self,
6540        node: Node<'_>,
6541        preprocessor_anchor: Node<'_>,
6542        walk: impl FnOnce(&mut Self) -> T,
6543    ) -> T {
6544        use crate::graph::resolver::{
6545            enclosing_namespace_context, merge_preprocessor_guards,
6546            preprocessor_conditional_family_for_declaration_with_cache,
6547            preprocessor_conditional_family_node, preprocessor_guard_environment_with_cache,
6548        };
6549        let previous = self.active_source_context.take();
6550        let primary = node.id() == self.primary_root_id || self.primary_ancestry.contains(node);
6551        let inherited = (!primary).then_some(previous.as_ref()).flatten();
6552        let local_guards = preprocessor_guard_environment_with_cache(
6553            preprocessor_anchor,
6554            self.source,
6555            &mut self.preprocessor_context_cache,
6556        );
6557        let guards = match inherited {
6558            Some(outer) => outer
6559                .guards
6560                .as_ref()
6561                .zip(local_guards.as_ref())
6562                .and_then(|(outer, local)| merge_preprocessor_guards(outer, local)),
6563            None => local_guards,
6564        };
6565        let local_namespace = enclosing_namespace_context(node, self.source);
6566        let namespace = match (
6567            inherited.and_then(|outer| outer.child_namespace.as_ref()),
6568            local_namespace,
6569        ) {
6570            (Some(outer), Some(local)) => Some(format!("{outer}::{local}")),
6571            (Some(outer), None) => Some(outer.clone()),
6572            (None, local) => local,
6573        };
6574        let outer_lexical_scope =
6575            inherited.map_or_else(Vec::new, |outer| outer.child_lexical_scope.clone());
6576        let mut local_owners = Vec::new();
6577        let mut parent = node.parent();
6578        while let Some(ancestor) = parent {
6579            if matches!(
6580                ancestor.kind(),
6581                "namespace_definition"
6582                    | "class_specifier"
6583                    | "struct_specifier"
6584                    | "union_specifier"
6585                    | "enum_specifier"
6586            ) && let Some(name) = ancestor.child_by_field_name("name")
6587            {
6588                local_owners.push(node_text(name, self.source).to_owned());
6589            }
6590            parent = ancestor.parent();
6591        }
6592        local_owners.reverse();
6593        let mut child_lexical_scope = outer_lexical_scope.clone();
6594        child_lexical_scope.extend(local_owners);
6595        if matches!(
6596            node.kind(),
6597            "namespace_definition"
6598                | "class_specifier"
6599                | "struct_specifier"
6600                | "union_specifier"
6601                | "enum_specifier"
6602        ) && let Some(name) = node.child_by_field_name("name")
6603        {
6604            child_lexical_scope.push(node_text(name, self.source).to_owned());
6605        }
6606        let child_namespace = if node.kind() == "namespace_definition" {
6607            node.child_by_field_name("name")
6608                .map(|name| {
6609                    let local = node_text(name, self.source);
6610                    namespace
6611                        .as_ref()
6612                        .map_or_else(|| local.to_owned(), |outer| format!("{outer}::{local}"))
6613                })
6614                .or_else(|| namespace.clone())
6615        } else {
6616            namespace.clone()
6617        };
6618        let intern = |family: Node<'_>| {
6619            let mut collector = self.source_collector.borrow_mut();
6620            if family.id() == self.primary_root_id || self.primary_ancestry.contains(family) {
6621                collector.intern_node(family)
6622            } else {
6623                collector
6624                    .intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(family))
6625            }
6626        };
6627        let family = preprocessor_conditional_family_node(preprocessor_anchor)
6628            .map(intern)
6629            .or_else(|| inherited.and_then(|outer| outer.family));
6630        let exhaustive_family = preprocessor_conditional_family_for_declaration_with_cache(
6631            preprocessor_anchor,
6632            &mut self.preprocessor_context_cache,
6633        )
6634        .map(intern)
6635        .or_else(|| inherited.and_then(|outer| outer.exhaustive_family));
6636        let local_completion = crate::graph::resolver::callable_guard_completion_byte_with_cache(
6637            preprocessor_anchor,
6638            self.source,
6639            &mut self.preprocessor_context_cache,
6640        );
6641        let callable_guard_completion_byte = match inherited {
6642            Some(outer) => local_completion
6643                .zip(outer.callable_guard_completion_byte)
6644                .map(|(local, outer)| local.max(outer)),
6645            None => local_completion,
6646        };
6647        self.active_source_context = Some(CppActiveSourceContext {
6648            guards,
6649            namespace: child_namespace.clone(),
6650            child_namespace,
6651            outer_lexical_scope: child_lexical_scope.clone(),
6652            child_lexical_scope,
6653            family,
6654            exhaustive_family,
6655            callable_guard_completion_byte,
6656        });
6657        let result = walk(self);
6658        self.active_source_context = previous;
6659        result
6660    }
6661
6662    fn visit_node<'tree>(
6663        &mut self,
6664        node: Node<'tree>,
6665        scope: &ScopeInfo,
6666        stack: &mut Vec<CppWork<'tree>>,
6667        ancestry: &ParentIndex<'tree>,
6668    ) {
6669        if let Some(recovered_scope) = self.recovered_class_sibling_scopes.remove(&node.id()) {
6670            self.visit_node(node, &recovered_scope, stack, ancestry);
6671            return;
6672        }
6673        if let Some(recovered_scope) = self.recovered_namespace_scope(node, scope) {
6674            self.visit_node(node, &recovered_scope, stack, ancestry);
6675            return;
6676        }
6677        if scope.class_unit.is_some()
6678            && let Some(label) = cpp_macro_swallowed_access_label(node, self.source)
6679        {
6680            self.visit_macro_swallowed_access_label_members(node, &label, scope, ancestry);
6681            return;
6682        }
6683        // Fragmented-class recovery below may consume a malformed function
6684        // envelope before the ordinary kind dispatch runs. Recover any
6685        // export-macro class embedded in that envelope first; the strict class
6686        // head/base/body predicate is independent of which later recovery owns
6687        // the surrounding parser fragment.
6688        if node.kind() == "function_definition" && node.has_error() {
6689            self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6690        }
6691        if let Some(FragmentedClassRecovery {
6692            declaration_node: class_node,
6693            name,
6694            raw_supertypes,
6695            body: fragmented,
6696        }) = fragmented_class_body(node, self.source)
6697        {
6698            let displaced_namespace_items =
6699                displaced_fragment_namespace_geometry(node, self.source)
6700                    .map(|boundary| boundary.namespace_items)
6701                    .unwrap_or_default();
6702            let outcome = self.reparse_fragmented_export_class_members(&fragmented, &name);
6703            let mut class_stack = Vec::new();
6704            // When the full body cannot be safely reparsed, the original class
6705            // node still proves ownership for its parser-visible prefix.
6706            let parser_visible_body =
6707                (!matches!(&outcome, Some(FragmentedExportMembers::Complete(_))))
6708                    .then(|| cpp_body_node(class_node))
6709                    .flatten();
6710            let class_unit = self.visit_named_class_like_shape(
6711                class_node,
6712                name,
6713                parser_visible_body,
6714                true,
6715                Some(fragmented.class_range),
6716                Some(raw_supertypes),
6717                Some(extract_cpp_base_facts(class_node, self.source)),
6718                scope,
6719                &mut class_stack,
6720                ancestry,
6721            );
6722            let member_scope = ScopeInfo {
6723                package_name: class_unit.package_name().to_string(),
6724                module: scope.module.clone(),
6725                class_unit: Some(class_unit.clone()),
6726                template_signature: scope.template_signature.clone(),
6727                template_metadata: None,
6728                declarations_are_fields: true,
6729                recovered_specialization_member_scope: false,
6730                visible_using_namespaces: scope.visible_using_namespaces.clone(),
6731                local_block: None,
6732                visible_using_declarations: scope.visible_using_declarations.clone(),
6733            };
6734            let complete = outcome.is_some_and(|outcome| {
6735                self.visit_fragmented_export_class_members(node, outcome, class_unit, scope)
6736            });
6737            if complete {
6738                self.consumed_fragment_regions
6739                    .push((node.start_byte(), fragmented.class_range.end_byte));
6740            } else {
6741                // A macro-constrained member can make the full body reparse
6742                // unsafe while tree-sitter still exposes later class members
6743                // as bounded siblings up to the displaced `}`/`;`. Keep the
6744                // structurally proven class/base declaration and re-own those
6745                // sibling nodes under it. They retain their original parser
6746                // nodes and exact ranges; the close boundary comes solely from
6747                // `fragmented_class_body`.
6748                // Template wrappers put the escaped members beside the
6749                // template rather than beside its malformed declaration.
6750                for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
6751                    if candidate.start_byte() >= fragmented.reparse_end {
6752                        break;
6753                    }
6754                    if cpp_fragment_sibling_is_class_member(
6755                        candidate,
6756                        fragmented.reparse_end,
6757                        self.source,
6758                    ) {
6759                        self.recovered_class_sibling_scopes
6760                            .insert(candidate.id(), member_scope.clone());
6761                    }
6762                }
6763                // The macro-declaration grammar keeps the class head and its
6764                // parser-visible prefix members inside the fragmented
6765                // container itself, where there is no separate body node for
6766                // `visit_named_class_like_shape` to seed. Walk the container
6767                // with the member scope so those prefix members are owned by
6768                // the class; the head's bare tokens mint nothing on their own.
6769                if class_node.kind() == "ERROR" && class_node.has_error() {
6770                    stack.push(CppWork::Container(CppContainer {
6771                        node: class_node,
6772                        scope: member_scope,
6773                    }));
6774                }
6775            }
6776            for item in displaced_namespace_items {
6777                self.recovered_class_sibling_scopes
6778                    .insert(item.id(), scope.clone());
6779            }
6780            stack.extend(class_stack);
6781            return;
6782        }
6783        if self.visit_folded_aggregate(node, scope) {
6784            return;
6785        }
6786        match node.kind() {
6787            "template_declaration" => {
6788                if let Some(recovered) =
6789                    recover_fragmented_preprocessor_class(node, self.source, ancestry)
6790                {
6791                    let mut template_scope = scope.clone();
6792                    template_scope.template_signature =
6793                        cpp_template_signature(node, recovered.declaration_node, self.source);
6794                    template_scope.template_metadata =
6795                        cpp_template_metadata(node, recovered.class_node, self.source, ancestry);
6796                    let raw_supertypes =
6797                        Some(extract_cpp_supertypes(recovered.class_node, self.source));
6798                    let mut class_stack = Vec::new();
6799                    let class_unit = self.visit_named_class_like_shape(
6800                        recovered.class_node,
6801                        recovered.name,
6802                        Some(recovered.body),
6803                        true,
6804                        Some(recovered.range),
6805                        raw_supertypes,
6806                        Some(extract_cpp_base_facts(recovered.class_node, self.source)),
6807                        &template_scope,
6808                        &mut class_stack,
6809                        ancestry,
6810                    );
6811                    self.parsed.record_materialization(
6812                        MaterializationRecord::RecoveredDeclaration {
6813                            recovery: recovered.range,
6814                            unit: class_unit.clone(),
6815                        },
6816                    );
6817                    let member_scope = ScopeInfo {
6818                        package_name: template_scope.package_name.clone(),
6819                        module: template_scope.module.clone(),
6820                        class_unit: Some(class_unit.clone()),
6821                        template_signature: template_scope.template_signature.clone(),
6822                        template_metadata: None,
6823                        declarations_are_fields: true,
6824                        recovered_specialization_member_scope: recovered
6825                            .class_node
6826                            .child_by_field_name("name")
6827                            .is_some_and(|name| name.kind() == "template_type"),
6828                        visible_using_namespaces: template_scope.visible_using_namespaces.clone(),
6829                        local_block: None,
6830                        visible_using_declarations: template_scope
6831                            .visible_using_declarations
6832                            .clone(),
6833                    };
6834                    for tail_member in recovered.tail_members.into_iter().rev() {
6835                        stack.push(CppWork::Node(CppNodeWork {
6836                            node: tail_member,
6837                            scope: member_scope.clone(),
6838                        }));
6839                    }
6840                    stack.extend(class_stack);
6841                    for sibling in recovered.member_siblings {
6842                        self.recovered_class_sibling_scopes
6843                            .insert(sibling.id(), member_scope.clone());
6844                    }
6845                    return;
6846                }
6847                for index in (0..node.named_child_count()).rev() {
6848                    let Some(child) = node.named_child(index) else {
6849                        continue;
6850                    };
6851                    if matches!(
6852                        child.kind(),
6853                        "class_specifier"
6854                            | "struct_specifier"
6855                            | "union_specifier"
6856                            | "enum_specifier"
6857                            | "function_definition"
6858                            | "declaration"
6859                            | "field_declaration"
6860                            | "alias_declaration"
6861                            | "namespace_definition"
6862                            // `template <class T> template <class U> void
6863                            // Box<T>::take(U) {}`: an out-of-line member
6864                            // template nests one head inside another. The
6865                            // inner head is the member's own and replaces the
6866                            // signature head when the nested node is visited.
6867                            | "template_declaration"
6868                    ) {
6869                        let mut template_scope = scope.clone();
6870                        template_scope.template_signature =
6871                            cpp_template_signature(node, child, self.source);
6872                        template_scope.template_metadata =
6873                            cpp_template_metadata(node, child, self.source, ancestry);
6874                        if let Some(recovered) = recover_fragmented_partial_specialization(
6875                            node,
6876                            child,
6877                            self.source,
6878                            ancestry,
6879                        ) {
6880                            let code_unit = self.visit_named_class_like_shape(
6881                                recovered.declaration_node,
6882                                recovered.name,
6883                                None,
6884                                true,
6885                                Some(recovered.range),
6886                                None,
6887                                None,
6888                                &template_scope,
6889                                stack,
6890                                ancestry,
6891                            );
6892                            self.parsed.record_materialization(
6893                                MaterializationRecord::RecoveredDeclaration {
6894                                    recovery: recovered.range,
6895                                    unit: code_unit.clone(),
6896                                },
6897                            );
6898                            let mut member_scope = template_scope.clone();
6899                            member_scope.class_unit = Some(code_unit);
6900                            member_scope.declarations_are_fields = true;
6901                            member_scope.recovered_specialization_member_scope = true;
6902                            for prefix_member in recovered.prefix_members.into_iter().rev() {
6903                                stack.push(CppWork::Node(CppNodeWork {
6904                                    node: prefix_member,
6905                                    scope: member_scope.clone(),
6906                                }));
6907                            }
6908                            for sibling in recovered.member_siblings {
6909                                self.recovered_class_sibling_scopes
6910                                    .insert(sibling.id(), member_scope.clone());
6911                            }
6912                            for following in recovered.following_declarations.into_iter().rev() {
6913                                stack.push(CppWork::Node(CppNodeWork {
6914                                    node: following,
6915                                    scope: scope.clone(),
6916                                }));
6917                            }
6918                            return;
6919                        }
6920                        stack.push(CppWork::Node(CppNodeWork {
6921                            node: child,
6922                            scope: template_scope,
6923                        }));
6924                    }
6925                }
6926            }
6927            "namespace_definition" => self.visit_namespace(node, scope, stack, ancestry),
6928            "linkage_specification" => {
6929                if let Some(body) = cpp_body_node(node) {
6930                    // A linkage specification either wraps a direct declaration or a
6931                    // braced declaration_list. Visit the direct item itself so
6932                    // declaration recoveries still run on its original syntax node.
6933                    let work = if body.kind() == "declaration_list" {
6934                        CppWork::Container(CppContainer {
6935                            node: body,
6936                            scope: scope.clone(),
6937                        })
6938                    } else {
6939                        CppWork::Node(CppNodeWork {
6940                            node: body,
6941                            scope: scope.clone(),
6942                        })
6943                    };
6944                    stack.push(work);
6945                } else {
6946                    stack.push(CppWork::Container(CppContainer {
6947                        node,
6948                        scope: scope.clone(),
6949                    }));
6950                }
6951            }
6952            "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier" => {
6953                self.visit_class_like(node, scope, stack, ancestry)
6954            }
6955            "function_definition" => self.visit_function_definition(node, scope, stack, ancestry),
6956            // A bare namespace-begin sentinel can make tree-sitter promote the
6957            // wrapped declaration to an ERROR node instead of the usual bogus
6958            // function_definition envelope. Keep the recovery entry point on
6959            // the same structured path for both shapes; ordinary ERROR nodes
6960            // retain their declaration-preserving wrapper traversal when the
6961            // sentinel predicate does not match.
6962            "ERROR" => {
6963                // A standalone field-list invocation has no declaration wrapper.
6964                // Claim only an envelope consisting entirely of known invocations.
6965                if node.named_child_count() > 0
6966                    && node.children(&mut node.walk()).all(|child| {
6967                        child.kind() == "identifier"
6968                            && self
6969                                .object_macro_fields
6970                                .contains_key(node_text(child, self.source))
6971                    })
6972                    && self.visit_bare_object_macro_fields(node, scope)
6973                {
6974                    return;
6975                }
6976                self.visit_object_macro_error_classes(node, scope);
6977                if !self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
6978                    self.visit_embedded_function_like_export_classes(node, scope, stack, ancestry);
6979                    if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
6980                        return;
6981                    }
6982                    if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
6983                        return;
6984                    }
6985                    self.visit_macro_swallowed_function_declarations(node, scope);
6986                    self.visit_macro_wrapped_declarations(node, scope, ancestry);
6987                    self.visit_stranded_class_members(node, scope, ancestry);
6988                    stack.push(CppWork::Container(CppContainer {
6989                        node,
6990                        scope: scope.clone(),
6991                    }));
6992                }
6993            }
6994            "declaration" => {
6995                if node.has_error() {
6996                    // Object-like macro lines before a function-like
6997                    // export-macro class head keep the head in a `declaration`
6998                    // rather than in an `ERROR` (#2924). It is the same head
6999                    // and the same following-sibling body, so it takes the same
7000                    // recovery; without it the head's macro invocation indexes
7001                    // as a field named after the macro.
7002                    if self.visit_function_like_export_class_pair(node, scope, stack, ancestry) {
7003                        return;
7004                    }
7005                }
7006                // The macro-declaration grammar reads a K&R prototype macro
7007                // line as a clean declaration with the name and the macro
7008                // displaced into `macro_modifier` slots, so the prototype
7009                // candidate gate runs on clean declarations too; the candidates
7010                // themselves remain the admission proof.
7011                self.visit_prototype_macro_declarations(node, scope, ancestry);
7012                if self.node_is_inside_consumed_fragment(node) {
7013                    // The whole declaration was a recovered K&R
7014                    // prototype macro invocation (issue #2932); the
7015                    // reparse above already indexed its Function.
7016                    // Falling through to the ordinary declaration
7017                    // visitor would additionally mint the swapped-field
7018                    // wreckage this recovery exists to replace.
7019                    return;
7020                }
7021                if scope.class_unit.is_some()
7022                    && scope.declarations_are_fields
7023                    && scope.recovered_specialization_member_scope
7024                    && let Some(alias_name) =
7025                        recovered_using_declaration_alias_name(node, self.source)
7026                {
7027                    self.add_type_aliases(ancestry, node, scope, vec![alias_name]);
7028                } else {
7029                    self.visit_declaration(
7030                        node,
7031                        scope,
7032                        scope.declarations_are_fields,
7033                        stack,
7034                        ancestry,
7035                    )
7036                }
7037            }
7038            // A K&R prototype macro invocation with a pointer return type and a
7039            // simple argument list parses with no ERROR node at all: tree-sitter
7040            // reads `T *name _(( args ));` as a multiplication of a type by a
7041            // call (`T * name_(...)`), wrapped in an `expression_statement`
7042            // that `has_error()` only because of a `MISSING "::"` inside it
7043            // (issue #2932). Nothing else mints a declaration from an
7044            // `expression_statement` at declaration scope today, so there is
7045            // no existing behavior to preserve here if this node is not the
7046            // K&R shape; the admission gate inside
7047            // `visit_prototype_macro_declarations` (exactly one clean function
7048            // prototype spanning the recovered run) is what keeps this arm
7049            // safe on an ordinary errorful expression statement that happens
7050            // to contain nested parentheses.
7051            "expression_statement" => {
7052                if node.has_error() {
7053                    self.visit_prototype_macro_declarations(node, scope, ancestry);
7054                }
7055            }
7056            "field_declaration" => {
7057                // A class-body K&R prototype macro line is the same
7058                // `macro_modifier` shape as a file-scope one, so it takes the
7059                // same candidate gate and reparse (#6902). Only this node's own
7060                // recovery ends its visit: a recovery walk can reach members of
7061                // a region another recovery consumed, and those stay walked.
7062                if !self.visit_prototype_macro_declarations(node, scope, ancestry) {
7063                    self.visit_declaration(node, scope, true, stack, ancestry)
7064                }
7065            }
7066            "preproc_call" => self.visit_preproc_call(node),
7067            "type_definition" | "alias_declaration" => {
7068                self.visit_type_declaration(node, scope, stack, ancestry)
7069            }
7070            "preproc_def" | "preproc_function_def" => self.visit_macro(ancestry, node),
7071            // `#include` is collected by the coordinated primary events before the
7072            // walk, so a directive the container walk never reaches -- inside
7073            // a class body (Eigen's `EIGEN_DENSEBASE_PLUGIN`) or a switch
7074            // statement (llama.cpp's `sycl/info/aspects.def`) -- is still an
7075            // include claim.
7076            "preproc_include" => {}
7077            // The grammar represents a split extern-C condition as sibling
7078            // markers; declarations between them remain unconditional file
7079            // scope items and need no special declaration metadata.
7080            "preproc_linkage_open" | "preproc_linkage_close" => {}
7081            kind if preserves_declaration_scope_through_wrapper(
7082                kind,
7083                scope.class_unit.is_some(),
7084            ) =>
7085            {
7086                // A preprocessor conditional gates every declaration inside it
7087                // on a configuration this analyzer never evaluates; record the
7088                // interval so declaration state can say so (issue #1476). The
7089                // else/elif branches are children of the `preproc_if` node, so
7090                // recording the openers covers every branch.
7091                if kind == "labeled_statement" {
7092                    self.visit_access_label_constructor(node, scope, ancestry);
7093                }
7094                if matches!(kind, "preproc_if" | "preproc_ifdef" | "preproc_ifndef") {
7095                    let mut range = cpp_declaration_range(node);
7096                    if let Some(boundary) = cpp_displaced_preprocessor_boundary(node) {
7097                        range.end_byte = boundary.end_byte;
7098                        range.end_line = boundary.end_line;
7099                    }
7100                    self.parsed.record_materialization(
7101                        MaterializationRecord::ConfigurationConditional { range },
7102                    );
7103                    if node.has_error() {
7104                        // A malformed export-macro class can close the namespace
7105                        // node early while the enclosing include guard still owns
7106                        // the remaining class-head/body pairs. The ordinary walk
7107                        // cannot carry the lost namespace through those promoted
7108                        // siblings. Scan only structured ERROR nodes in this
7109                        // already-malformed conditional; the pair recovery's
7110                        // exact class/macro/body predicate remains the admission
7111                        // gate, and its namespace lifting restores the owner.
7112                        let mut candidates = vec![node];
7113                        while let Some(candidate) = candidates.pop() {
7114                            // An `ERROR` still inside a namespace body is one
7115                            // the ordinary walk reaches with that namespace in
7116                            // scope. Claiming it here registers the recovered
7117                            // class at file scope and the consumed region then
7118                            // suppresses the walk that would have named it
7119                            // correctly -- which is why Botan's `DL_Group` was
7120                            // `DL_Group` and not `Botan.DL_Group` in the real
7121                            // header, where an include guard wraps the
7122                            // namespace, and was right in a fixture without one
7123                            // (#2552).
7124                            if candidate.kind() == "ERROR"
7125                                && !cpp_is_inside_namespace_body(candidate, ancestry)
7126                                && self.visit_function_like_export_class_pair(
7127                                    candidate, scope, stack, ancestry,
7128                                )
7129                            {
7130                                continue;
7131                            }
7132                            for index in (0..candidate.named_child_count()).rev() {
7133                                candidates.push(
7134                                    candidate
7135                                        .named_child(index)
7136                                        .expect("index below the node's own named child count"),
7137                                );
7138                            }
7139                        }
7140                    }
7141                }
7142                stack.push(CppWork::Container(CppContainer {
7143                    node,
7144                    scope: scope.clone(),
7145                }))
7146            }
7147            // A macro invocation the parser could not group leaves its head in
7148            // whatever slot was free. The `function_definition` and `ERROR`
7149            // arms above try the same recovery on the envelopes they get; a
7150            // flattened run leaves a bare `identifier`/`type_identifier`, and a
7151            // swallowed tail can land in a `parameter_declaration` (#3094).
7152            _ => {
7153                self.visit_collapsed_macro_declaration_run(node, scope, ancestry);
7154            }
7155        }
7156    }
7157
7158    fn visit_macro_swallowed_function_declarations<'tree>(
7159        &mut self,
7160        envelope: Node<'tree>,
7161        scope: &ScopeInfo,
7162    ) {
7163        if !cpp_macro_swallowed_declaration_envelope(envelope, self.source)
7164            || envelope.kind() == "ERROR"
7165                && envelope
7166                    .parent()
7167                    .is_some_and(|parent| parent.kind() == "ERROR")
7168        {
7169            return;
7170        }
7171        let mut stack = (0..envelope.named_child_count())
7172            .filter_map(|index| envelope.named_child(index))
7173            .collect::<Vec<_>>();
7174        while let Some(node) = stack.pop() {
7175            if node.kind() == "function_declarator" {
7176                self.visit_error_swallowed_function_declaration(node, scope);
7177            }
7178            for child in named_children_iter(node) {
7179                stack.push(child);
7180            }
7181        }
7182    }
7183
7184    /// Index the declarations an attribute-like macro invocation swallowed into
7185    /// a declaration-scope `ERROR` node. See [`macro_wrapped_declarations`] for
7186    /// the shape and why the parser produces it.
7187    fn visit_macro_wrapped_declarations<'tree>(
7188        &mut self,
7189        envelope: Node<'tree>,
7190        scope: &ScopeInfo,
7191        ancestry: &ParentIndex<'tree>,
7192    ) {
7193        let recovered = macro_wrapped_declarations(envelope, self.source, ancestry);
7194        if recovered.is_empty() {
7195            return;
7196        }
7197        let recovery = cpp_recovery_window(self.source, envelope.start_byte(), envelope.end_byte());
7198        self.record_recovered_declarations(recovery, envelope, |visitor| {
7199            for declaration in recovered {
7200                visitor.add_macro_wrapped_declaration(declaration, scope, ancestry);
7201            }
7202        });
7203    }
7204
7205    /// Index the declarations a macro invocation collapsed. See
7206    /// [`collapsed_macro_declaration_run`] for the shape and why the parser
7207    /// produces it. Returns whether it claimed the region beginning at
7208    /// `envelope`.
7209    ///
7210    /// The parser's own nodes cannot be read the way the swallowed tail of the
7211    /// one-line shape can. Of the 214 declarations whisper's `llama.h` hides in
7212    /// its envelope, 57 are shredded to bare identifier and punctuation tokens
7213    /// with no declarator left at all, and the declarators that do survive on
7214    /// the envelope's declarator spine pair one declaration's name with the
7215    /// *next* declaration's parameter list. Reading those would be a guess.
7216    ///
7217    /// The bytes are still ordinary declarations, though, and the collapse is
7218    /// the parser carrying the failure forward from one macro invocation. So
7219    /// reparse the region, walk the items the parser makes of it, and when one
7220    /// item is itself a collapsed run, recover that invocation from its own
7221    /// bytes and resume the scan just past it. Each pass starts later than the
7222    /// last, so the scan is a loop over the invocations that collapse, not over
7223    /// the declarations: `llama.h` needs three passes for 214 declarations.
7224    ///
7225    /// The region can reach past `envelope` into the siblings the parser handed
7226    /// the rest of one invocation, so it is recorded as consumed: those
7227    /// siblings are this recovery's, and the ordinary walk must not read them
7228    /// again. Recording it after the scan, not before, keeps the scan's own
7229    /// reparsed nodes -- which carry their original byte offsets -- visible to
7230    /// the walk it drives.
7231    fn visit_collapsed_macro_declaration_run<'tree>(
7232        &mut self,
7233        envelope: Node<'tree>,
7234        scope: &ScopeInfo,
7235        ancestry: &ParentIndex<'tree>,
7236    ) -> bool {
7237        let Some(run) = collapsed_macro_declaration_run(envelope, self.source, ancestry) else {
7238            return false;
7239        };
7240        let start = envelope.start_byte();
7241        let end = run.region_end;
7242        let recovery = cpp_recovery_window(self.source, start, end);
7243        self.record_recovered_declarations(recovery, envelope, |visitor| {
7244            let mut position = start;
7245            while position < end {
7246                let Some(tree) = cpp_reparse_region_items(visitor.source, position, end) else {
7247                    return;
7248                };
7249                let root = tree.root_node();
7250                // A region reparse is its own tree and needs its own parent
7251                // index and dependency arena; the caller's answers nothing
7252                // about these nodes.
7253                let ancestry = visitor.recovery_ancestry(root);
7254                let mut cursor = root.walk();
7255                let collapsed =
7256                    root.named_children(&mut cursor)
7257                        .enumerate()
7258                        .find_map(|(index, item)| {
7259                            collapsed_macro_declaration_run(item, visitor.source, &ancestry)
7260                                .map(|run| (index, item, run))
7261                        });
7262                // Walk only the items before the collapsed one. It swallowed
7263                // the rest of the region, so handing it to the ordinary walk
7264                // would re-enter this recovery on a region that starts where
7265                // this one did.
7266                let mut stack = Vec::new();
7267                push_cpp_sibling_range(
7268                    root,
7269                    0,
7270                    collapsed.as_ref().map_or(usize::MAX, |(index, ..)| *index),
7271                    scope.clone(),
7272                    &mut stack,
7273                );
7274                visitor.drain_cpp_work(stack, &ancestry);
7275                let Some((_, item, run)) = collapsed else {
7276                    return;
7277                };
7278                // On its own bytes the invocation is the declaration-scope
7279                // shape `macro_wrapped_declarations` reads, so its wrapped
7280                // declaration needs no reader of its own here.
7281                if let Some(head) =
7282                    cpp_reparse_region_items(visitor.source, item.start_byte(), run.invocation_end)
7283                {
7284                    let head_root = head.root_node();
7285                    let head_ancestry = visitor.recovery_ancestry(head_root);
7286                    visitor.run_container_work(head_root, scope.clone(), &head_ancestry);
7287                }
7288                assert!(
7289                    run.invocation_end > position,
7290                    "a collapsed run at {position} must end after the byte the scan resumed \
7291                     from, but ended at {}",
7292                    run.invocation_end
7293                );
7294                position = run.invocation_end;
7295            }
7296        });
7297        self.consumed_fragment_regions.push((start, end));
7298        true
7299    }
7300
7301    /// Index the members written under an access label that a class-scope
7302    /// macro invocation swallowed. See [`cpp_macro_swallowed_access_label`]
7303    /// for the shape.
7304    ///
7305    /// The members are whole in the source but not in this tree, and at file
7306    /// scope a bodiless member such as `Ctor(int a);` reads as a variable
7307    /// declaration, so the reparse keeps the class around them: the class head
7308    /// through its `{`, the label and its members, and the class's `}`, as one
7309    /// included-range parse at the original offsets. That is the parse the
7310    /// class gets with the invocation's bytes left out, which is what the
7311    /// replacement's own declarations would have to be read without anyway.
7312    /// The reparsed label is a real `access_specifier`, so the members carry the
7313    /// access they are written under. Only the members the label swallowed are
7314    /// walked; the class's other members are the ordinary walk's. When the
7315    /// reparse does not read the label back, nothing is published: the
7316    /// field this node would otherwise mint is named after the access keyword.
7317    fn visit_macro_swallowed_access_label_members<'tree>(
7318        &mut self,
7319        node: Node<'tree>,
7320        label: &CppSwallowedAccessLabel<'tree>,
7321        scope: &ScopeInfo,
7322        ancestry: &ParentIndex<'tree>,
7323    ) {
7324        let Some(body) = ancestry.parent(node) else {
7325            return;
7326        };
7327        let Some(class_like) = ancestry.parent(body) else {
7328            return;
7329        };
7330        // The reparse holds the class itself, so the recovery's source context
7331        // is the class's enclosing one; the class's own scope would name it
7332        // twice in the recovered members' lexical paths.
7333        let Some(class_parent) = ancestry.parent(class_like) else {
7334            return;
7335        };
7336        let (Some(open), Some(close)) = (body.child(0), body.child(body.child_count() - 1)) else {
7337            return;
7338        };
7339        if body.kind() != "field_declaration_list"
7340            || open.kind() != "{"
7341            || close.kind() != "}"
7342            || close.is_missing()
7343        {
7344            return;
7345        }
7346        let start = label.keyword.start_byte();
7347        let end = node.end_byte();
7348        let Some(tree) = parse_source_ranges_with_cancellation(
7349            &tree_sitter_cpp::LANGUAGE.into(),
7350            self.source,
7351            &[
7352                (class_like.start_byte(), open.end_byte()),
7353                (start, end),
7354                (close.start_byte(), close.end_byte()),
7355            ],
7356            None,
7357        ) else {
7358            return;
7359        };
7360        let root = tree.root_node();
7361        let Some(reparsed_body) = root
7362            .descendant_for_byte_range(open.start_byte(), open.end_byte())
7363            .and_then(|brace| brace.parent())
7364            .filter(|parent| parent.kind() == "field_declaration_list")
7365        else {
7366            return;
7367        };
7368        let mut cursor = reparsed_body.walk();
7369        let members = reparsed_body
7370            .named_children(&mut cursor)
7371            .enumerate()
7372            .filter(|(_, member)| member.start_byte() >= start && member.end_byte() <= end)
7373            .map(|(index, member)| (index, member.kind(), member.start_byte()))
7374            .collect::<Vec<_>>();
7375        let Some(&(first, "access_specifier", label_start)) = members.first() else {
7376            return;
7377        };
7378        if label_start != start {
7379            return;
7380        }
7381        let last = members.last().expect("the label is a member").0;
7382        let reparsed_ancestry = self.recovery_ancestry(root);
7383        let recovery = cpp_recovery_window(self.source, start, end);
7384        self.record_recovered_declarations(recovery, class_parent, |visitor| {
7385            let mut stack = Vec::new();
7386            push_cpp_sibling_range(
7387                reparsed_body,
7388                first + 1,
7389                last + 1,
7390                scope.clone(),
7391                &mut stack,
7392            );
7393            visitor.drain_cpp_work(stack, &reparsed_ancestry);
7394        });
7395    }
7396
7397    /// Index the members a string-argument attribute macro stranded in an
7398    /// `ERROR` inside a class body.
7399    ///
7400    /// `BOTAN_DEPRECATED("text") explicit Ctor(T);` costs the parser the
7401    /// grouping of the attributed member and of every member written after it
7402    /// until it recovers. The members are still whole `function_declarator`
7403    /// nodes; [`stranded_declaration_run`] regroups them. Without this, an
7404    /// export-macro class such as Botan's `DL_Group` was indexed with no
7405    /// members at all (#2552).
7406    fn visit_stranded_class_members<'tree>(
7407        &mut self,
7408        node: Node<'tree>,
7409        scope: &ScopeInfo,
7410        ancestry: &ParentIndex<'tree>,
7411    ) {
7412        if scope.class_unit.is_none() || !scope.declarations_are_fields {
7413            return;
7414        }
7415        for member in stranded_declaration_run(node, self.source).declarations {
7416            self.add_macro_wrapped_declaration(member, scope, ancestry);
7417        }
7418    }
7419
7420    /// Index the constructor an access label swallowed in a reparsed class
7421    /// body. See [`cpp_access_label_constructor_call_start`] for the shape.
7422    ///
7423    /// The declarator is recovered by reparsing from that call to the end of
7424    /// the label's own statement, which is the same offset-preserving region
7425    /// reparse every other recovery here uses, so the recovered nodes carry
7426    /// their true source positions.
7427    fn visit_access_label_constructor<'tree>(
7428        &mut self,
7429        node: Node<'tree>,
7430        scope: &ScopeInfo,
7431        ancestry: &ParentIndex<'tree>,
7432    ) {
7433        let Some(class_unit) = scope.class_unit.clone() else {
7434            return;
7435        };
7436        if !scope.declarations_are_fields {
7437            return;
7438        }
7439        let class_name = class_unit.identifier().to_string();
7440        // Statement context reads pointer parameters as multiplication. Use the
7441        // same class-context validation as an unlabelled constructor statement.
7442        let statement = node.named_children(&mut node.walk()).find(|child| {
7443            cpp_reparsed_bare_constructor_call(*child, &class_name, self.source).is_some()
7444        });
7445        if let Some(statement) = statement {
7446            self.visit_bare_constructor_expression(statement, scope, ancestry);
7447            return;
7448        }
7449        let Some(start) = cpp_access_label_constructor_call_start(node, &class_name, self.source)
7450        else {
7451            return;
7452        };
7453        let Some(tree) = cpp_reparse_region_items(self.source, start, node.end_byte()) else {
7454            return;
7455        };
7456        let root = tree.root_node();
7457        let Some(declarator) =
7458            cpp_reparsed_exact_constructor_declarator(root, start, &class_name, self.source)
7459        else {
7460            return;
7461        };
7462        let reparsed_ancestry = self.recovery_ancestry(root);
7463        let definition = cpp_declarator_function_definition(declarator, &reparsed_ancestry);
7464        let range = cpp_declaration_range(definition.unwrap_or(declarator));
7465        let recovery = cpp_recovery_window(self.source, start, node.end_byte());
7466        self.record_recovered_declarations(recovery, node, |visitor| {
7467            visitor.add_macro_wrapped_declaration(
7468                MacroWrappedDeclaration {
7469                    declarator,
7470                    range,
7471                    is_static: false,
7472                },
7473                scope,
7474                &reparsed_ancestry,
7475            );
7476        });
7477        // This constructor has been accounted for. Its statement-context
7478        // initializer declarators do not declare additional members.
7479        self.consumed_fragment_regions.push((start, range.end_byte));
7480    }
7481
7482    /// Index the members a string-argument attribute macro stranded inside the
7483    /// `macro_type_specifier` the macro-declaration grammar reads them as (#2552
7484    /// shape 3 under the new grammar).
7485    ///
7486    /// `BOTAN_DEPRECATED("text") explicit Ctor(T); Ctor(U, V);` keeps the macro as
7487    /// a `macro_type_specifier` whose argument region is an `ERROR` holding the
7488    /// string, the attributed member as a `type_descriptor`, and its `;`, while
7489    /// the member after it becomes the specifier's own `type` field. Neither
7490    /// member has a real declarator node -- an abstract function declarator in a
7491    /// `type_descriptor` carries only parameters -- but each member's source range
7492    /// is whole, so reparse it and read the declarator from the reparse, which is
7493    /// the same offset-preserving region reparse every other recovery here uses.
7494    /// The admission gate is the same one every reparse shares: exactly one clean
7495    /// callable declaration spanning the member's range.
7496    fn visit_string_attribute_macro_type_members<'tree>(
7497        &mut self,
7498        node: Node<'tree>,
7499        scope: &ScopeInfo,
7500    ) -> bool {
7501        if node.kind() != "field_declaration" {
7502            return false;
7503        }
7504        let Some(specifier) = node
7505            .child_by_field_name("type")
7506            .filter(|type_node| type_node.kind() == "macro_type_specifier")
7507        else {
7508            return false;
7509        };
7510        if specifier
7511            .child_by_field_name("name")
7512            .is_none_or(|name| name.kind() != "identifier")
7513        {
7514            return false;
7515        }
7516        // The attributed member sits inside the argument `ERROR`: the string's
7517        // quote is the first child, the member's `;` the last, and its
7518        // type-descriptor is in between.
7519        let mut cursor = specifier.walk();
7520        let Some(argument_error) = specifier
7521            .named_children(&mut cursor)
7522            .find(|child| child.kind() == "ERROR")
7523        else {
7524            return false;
7525        };
7526        if argument_error
7527            .child(0)
7528            .is_none_or(|first| first.kind() != "\"" || first.is_missing())
7529            || argument_error
7530                .child(argument_error.child_count().saturating_sub(1))
7531                .is_none_or(|last| last.kind() != ";" || last.is_missing())
7532        {
7533            return false;
7534        }
7535        let mut members: Vec<((usize, usize), String)> = Vec::new();
7536        for index in 0..argument_error.child_count() {
7537            let Some(child) = argument_error.child(index) else {
7538                continue;
7539            };
7540            if child.kind() == "type_descriptor" {
7541                let start = (0..index)
7542                    .rev()
7543                    .find_map(|previous| {
7544                        argument_error
7545                            .child(previous)
7546                            .filter(|sibling| sibling.kind() == "explicit")
7547                    })
7548                    .map_or_else(|| child.start_byte(), |explicit| explicit.start_byte());
7549                let Some(name) = child
7550                    .child_by_field_name("type")
7551                    .and_then(|type_node| direct_identifier_name(type_node, self.source))
7552                else {
7553                    return false;
7554                };
7555                members.push(((start, argument_error.end_byte()), name));
7556            }
7557        }
7558        let [(first_range, first_name)] = members.as_slice() else {
7559            return false;
7560        };
7561        let Some(second) = specifier
7562            .child_by_field_name("type")
7563            .filter(|type_node| type_node.kind() == "type_descriptor")
7564        else {
7565            return false;
7566        };
7567        let Some(second_name) = second
7568            .child_by_field_name("type")
7569            .and_then(|type_node| direct_identifier_name(type_node, self.source))
7570        else {
7571            return false;
7572        };
7573        let members = [
7574            (*first_range, first_name.as_str()),
7575            ((second.start_byte(), node.end_byte()), second_name.as_str()),
7576        ];
7577        let recovery = cpp_recovery_window(self.source, node.start_byte(), node.end_byte());
7578        self.record_recovered_declarations(recovery, node, |visitor| {
7579            for ((start, end), member_name) in members {
7580                let Some(tree) = cpp_reparse_region_items(visitor.source, start, end) else {
7581                    continue;
7582                };
7583                let root = tree.root_node();
7584                // The file-scope reparse of a member-shaped head is errorful
7585                // (a member qualifier is not valid there), so the admission is
7586                // the declarator itself: exactly one function declarator
7587                // naming the member inside its own range.
7588                let mut candidate = None;
7589                let mut stack = vec![root];
7590                while let Some(current) = stack.pop() {
7591                    if current.kind() == "function_declarator"
7592                        && current.start_byte() >= start
7593                        && current.end_byte() <= end
7594                        && cpp_function_declarator_name_node(current)
7595                            .is_some_and(|name| node_text(name, visitor.source) == member_name)
7596                    {
7597                        if candidate.is_some() {
7598                            candidate = None;
7599                            break;
7600                        }
7601                        candidate = Some(current);
7602                        continue;
7603                    }
7604                    let mut cursor = current.walk();
7605                    stack.extend(current.named_children(&mut cursor));
7606                }
7607                let Some(declarator) = candidate else {
7608                    continue;
7609                };
7610                let reparsed_ancestry = visitor.recovery_ancestry(root);
7611                visitor.add_macro_wrapped_declaration(
7612                    MacroWrappedDeclaration {
7613                        declarator,
7614                        range: cpp_recovery_window(visitor.source, start, end),
7615                        is_static: false,
7616                    },
7617                    scope,
7618                    &reparsed_ancestry,
7619                );
7620            }
7621        });
7622        true
7623    }
7624
7625    fn add_macro_wrapped_declaration<'tree>(
7626        &mut self,
7627        declaration: MacroWrappedDeclaration<'tree>,
7628        scope: &ScopeInfo,
7629        ancestry: &ParentIndex<'tree>,
7630    ) {
7631        let Some(function) = extract_function_info(declaration.declarator, self.source, scope)
7632        else {
7633            return;
7634        };
7635        let code_unit =
7636            function.code_unit_with_synthetic(self.file.clone(), scope.class_unit.is_some());
7637        let definition = cpp_declarator_function_definition(declaration.declarator, ancestry);
7638        let is_constructor = scope
7639            .class_unit
7640            .as_ref()
7641            .is_some_and(|class| function.name == class.identifier())
7642            || cpp_callable_is_structural_constructor(
7643                declaration.declarator,
7644                self.source,
7645                ancestry,
7646            );
7647        let already_declared = self.parsed.contains_declaration(&code_unit);
7648        if already_declared {
7649            self.parsed
7650                .record_navigation_range(code_unit.clone(), declaration.range);
7651            if definition.is_none() {
7652                return;
7653            }
7654            // Preserve the earlier declaration-only occurrence while adding
7655            // the recovered body as a distinct definition occurrence.
7656        }
7657        self.add_declaration_with_range(
7658            ancestry,
7659            code_unit.clone(),
7660            declaration.range,
7661            Some(declaration.declarator),
7662            None,
7663            None,
7664        );
7665        if definition.is_some() {
7666            let recovered_declaration = *self
7667                .source_links
7668                .get(&code_unit)
7669                .and_then(|links| links.last())
7670                .expect("recovered definition source");
7671            let mut properties = self.source_properties.borrow_mut();
7672            let fact = properties
7673                .get_mut(&recovered_declaration)
7674                .expect("recovered definition source fact");
7675            fact.occurrence_role =
7676                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
7677            fact.callable_is_constructor |= is_constructor;
7678        }
7679        let signature = normalize_cpp_whitespace(
7680            self.source
7681                .get(declaration.range.start_byte..declaration.range.end_byte)
7682                .expect("a recovered declaration range covers one source range"),
7683        );
7684        let linkage = if declaration.is_static {
7685            CallableLinkage::Internal
7686        } else {
7687            cpp_callable_linkage(declaration.declarator, self.source, ancestry)
7688        };
7689        // Whether this is a definition is a property of the recovered node, not
7690        // of the caller: a declarator that a `function_definition` gives a body
7691        // is a definition wherever the recovery found it.
7692        let declaration_only = definition.is_none();
7693        self.add_callable_signature_with_metadata(
7694            code_unit.clone(),
7695            declaration.range.start_byte,
7696            declaration.declarator,
7697            ancestry,
7698            cpp_signature_metadata(
7699                signature,
7700                declaration.declarator,
7701                linkage,
7702                self.source,
7703                ancestry,
7704            )
7705            .with_declaration_only(declaration_only),
7706        );
7707        if already_declared {
7708            return;
7709        }
7710        if let Some(parent) = &scope.class_unit {
7711            self.parsed.add_child(parent.clone(), code_unit);
7712        } else if let Some(module) = &scope.module {
7713            self.parsed.add_child(module.clone(), code_unit);
7714        }
7715    }
7716
7717    /// Index the local types of an ordinary function body. Function bodies are
7718    /// otherwise outside the declaration walk, but their anonymous aggregate
7719    /// owners must exist so structured local binding inference can name their
7720    /// fields (#2994), and a named C++ local class and its members are query
7721    /// targets of their own (#6902).
7722    fn visit_local_types_in_function<'tree>(
7723        &mut self,
7724        function: Node<'tree>,
7725        callable: &CodeUnit,
7726        scope: &ScopeInfo,
7727        stack: &mut Vec<CppWork<'tree>>,
7728        ancestry: &ParentIndex<'tree>,
7729    ) {
7730        let Some(body) = cpp_body_node(function) else {
7731            return;
7732        };
7733        // A body opens a block scope, so nothing declared in it is a member of
7734        // the class the definition may be written inside. Dropping the
7735        // aggregate carrier gives an inline member function's body the same
7736        // block scope an out-of-line `void C::f()` body already has; without
7737        // it a C++ header, where member bodies are usually inline, would keep
7738        // none of these owners (#3301).
7739        let block_scope = ScopeInfo {
7740            class_unit: None,
7741            declarations_are_fields: false,
7742            local_block: Some(CppLocalBlock::new(callable, body)),
7743            ..scope.clone()
7744        };
7745        let scope = &block_scope;
7746        let mut pending = vec![body];
7747        while let Some(node) = pending.pop() {
7748            if matches!(node.kind(), "function_definition" | "lambda_expression") {
7749                continue;
7750            }
7751            if node.kind() == "declaration"
7752                && self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry)
7753            {
7754                continue;
7755            }
7756            if matches!(
7757                node.kind(),
7758                "class_specifier" | "struct_specifier" | "union_specifier"
7759            ) {
7760                self.visit_function_local_class(node, callable, scope, stack, ancestry);
7761                continue;
7762            }
7763            let mut cursor = node.walk();
7764            let mut children = node.named_children(&mut cursor).collect::<Vec<_>>();
7765            children.reverse();
7766            pending.extend(children);
7767        }
7768    }
7769
7770    /// Mint a named C++ class defined in a function body under the identity
7771    /// of the block it is written in (see [`CppLocalBlock`]). Its members are
7772    /// owned by the class through the ordinary class-body walk.
7773    ///
7774    /// C block-scope tags are a separate capability and keep their current
7775    /// absence. A header is read both ways, so a minted local class is a
7776    /// declaration whose existence differs between the two readings, which is
7777    /// what the C-reading witness records.
7778    fn visit_function_local_class<'tree>(
7779        &mut self,
7780        node: Node<'tree>,
7781        callable: &CodeUnit,
7782        scope: &ScopeInfo,
7783        stack: &mut Vec<CppWork<'tree>>,
7784        ancestry: &ParentIndex<'tree>,
7785    ) {
7786        if self.c_tag_semantics || cpp_body_node(node).is_none() {
7787            return;
7788        }
7789        let Some(name) = class_like_name(node, self.source, ancestry) else {
7790            return;
7791        };
7792        let block =
7793            std::iter::successors(ancestry.parent(node), |current| ancestry.parent(*current))
7794                .find(|ancestor| ancestor.kind() == "compound_statement")
7795                .expect("a function-local class is written inside the function body");
7796        self.c_tag_scope_witness = true;
7797        let local_scope = ScopeInfo {
7798            package_name: callable.package_name().to_string(),
7799            class_unit: None,
7800            template_signature: None,
7801            template_metadata: None,
7802            declarations_are_fields: false,
7803            recovered_specialization_member_scope: false,
7804            local_block: Some(CppLocalBlock::new(callable, block)),
7805            ..scope.clone()
7806        };
7807        self.visit_named_class_like(node, name, &local_scope, stack, ancestry);
7808    }
7809
7810    fn visit_error_swallowed_function_declaration<'tree>(
7811        &mut self,
7812        node: Node<'tree>,
7813        scope: &ScopeInfo,
7814    ) {
7815        let Some((start, end)) = cpp_error_swallowed_function_declaration_range(node) else {
7816            return;
7817        };
7818        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
7819            return;
7820        };
7821        let root = tree.root_node();
7822        let mut cursor = root.walk();
7823        let declarations = root
7824            .named_children(&mut cursor)
7825            .filter(|child| child.kind() != "comment")
7826            .collect::<Vec<_>>();
7827        let [declaration] = declarations.as_slice() else {
7828            return;
7829        };
7830        if declaration.kind() != "declaration"
7831            || declaration.has_error()
7832            || declaration.start_byte() != start
7833            || declaration.end_byte() != end
7834        {
7835            return;
7836        }
7837        let recovery = cpp_recovery_window(self.source, start, end);
7838        // The reparsed region is its own tree, so this walk indexes it itself.
7839        let reparsed_ancestry = self.recovery_ancestry(root);
7840        self.record_recovered_declarations(recovery, node, |visitor| {
7841            visitor.run_container_work(root, scope.clone(), &reparsed_ancestry);
7842        });
7843    }
7844
7845    /// Recover a K&R-compatibility prototype macro invocation such as
7846    /// ruby.h's `RET name _(( args ));` (issue #2932). The `_` macro --
7847    /// spelled `__P`, `OF`, or `PROTO` in other pre-ANSI-C codebases -- is
7848    /// defined only in headers outside this workspace (ruby's own
7849    /// `ruby/defines.h`), so tree-sitter-cpp has no grammar production for
7850    /// `name _((args))` and turns each such line into a malformed
7851    /// `declaration` or pointer-expression statement. Before this
7852    /// recovery, the ordinary declaration visitor read that wreckage as a
7853    /// field with the type and name swapped (e.g. a field literally named
7854    /// `VALUE`) and indexed no prototype for the real name at all.
7855    ///
7856    /// [`cpp_prototype_macro_candidates`] admits only parser-owned shapes that
7857    /// preserve the declared name, a known one-argument compatibility macro,
7858    /// the nested argument list, and a live terminator. It rejects arbitrary
7859    /// identifiers and bare `ERROR` envelopes that flattened those relations;
7860    /// guessing there previously turned malformed non-macro declarations into
7861    /// invented functions. For each proven candidate this reparses the range
7862    /// before the macro token, the inner parenthesized argument node, and the
7863    /// terminating `;` as one included-range parse. That is the parser-native
7864    /// equivalent of expanding `#define _(args) args` while retaining original
7865    /// byte offsets. The result is admitted only when it is exactly one clean
7866    /// function prototype spanning the recovered run.
7867    ///
7868    /// A class-body line is a `field_declaration`, and a bodiless member read
7869    /// at file scope loses the class-body position its `static` and access
7870    /// facts depend on. That reparse therefore keeps the class around the
7871    /// prototype, as [`PrototypeMacroClassEnvelope`] describes, and admits
7872    /// exactly one clean member prototype in the reparsed body instead.
7873    ///
7874    /// Returns whether a candidate was recovered.
7875    fn visit_prototype_macro_declarations<'tree>(
7876        &mut self,
7877        node: Node<'tree>,
7878        scope: &ScopeInfo,
7879        ancestry: &ParentIndex<'tree>,
7880    ) -> bool {
7881        let candidates = cpp_prototype_macro_candidates(node, self.source);
7882        if candidates.is_empty() {
7883            return false;
7884        }
7885        let envelope = if node.kind() == "field_declaration" {
7886            let Some(envelope) = cpp_prototype_macro_class_envelope(node, self.source, ancestry)
7887            else {
7888                return false;
7889            };
7890            Some(envelope)
7891        } else {
7892            None
7893        };
7894        let mut recovered = false;
7895        for candidate in candidates {
7896            let start = candidate.run_start;
7897            let end = candidate.semicolon_end;
7898            if self.byte_range_is_inside_consumed_fragment(start, end) {
7899                continue;
7900            }
7901            let mut ranges = Vec::with_capacity(6);
7902            if let Some(envelope) = &envelope {
7903                ranges.push(envelope.head);
7904                ranges.extend(envelope.label);
7905            }
7906            ranges.extend(candidate.ranges());
7907            if let Some(envelope) = &envelope {
7908                ranges.push(envelope.close);
7909            }
7910            let Some(tree) = parse_source_ranges_with_cancellation(
7911                &tree_sitter_cpp::LANGUAGE.into(),
7912                self.source,
7913                &ranges,
7914                None,
7915            ) else {
7916                continue;
7917            };
7918            let root = tree.root_node();
7919            let (container, expected_kind) = match &envelope {
7920                None => (root, "declaration"),
7921                Some(envelope) => {
7922                    let Some(body) = root
7923                        .descendant_for_byte_range(envelope.open.0, envelope.open.1)
7924                        .and_then(|brace| brace.parent())
7925                        .filter(|parent| parent.kind() == "field_declaration_list")
7926                    else {
7927                        continue;
7928                    };
7929                    (body, "field_declaration")
7930                }
7931            };
7932            // Everything the envelope contributes ends before the prototype.
7933            let mut cursor = container.walk();
7934            let declarations = container
7935                .named_children(&mut cursor)
7936                .enumerate()
7937                .filter(|(_, child)| child.kind() != "comment" && child.end_byte() > start)
7938                .collect::<Vec<_>>();
7939            let [(index, declaration)] = declarations.as_slice() else {
7940                continue;
7941            };
7942            if declaration.kind() != expected_kind
7943                || declaration.has_error()
7944                || declaration.start_byte() != start
7945                || declaration.end_byte() != end
7946                || declaration
7947                    .child_by_field_name("declarator")
7948                    .and_then(extract_function_declarator)
7949                    .is_none()
7950            {
7951                continue;
7952            }
7953            let index = *index;
7954            let recovery = cpp_recovery_window(self.source, start, end);
7955            // The reparsed region is its own tree, so this walk indexes it
7956            // itself. A class envelope holds the class, so the recovery's
7957            // source context is the class's enclosing one; the class's own
7958            // would name it twice in the member's lexical path.
7959            let reparsed_ancestry = self.recovery_ancestry(root);
7960            let origin = envelope
7961                .as_ref()
7962                .map_or(node, |envelope| envelope.class_parent);
7963            self.record_recovered_declarations(recovery, origin, |visitor| {
7964                let mut stack = Vec::new();
7965                push_cpp_sibling_range(container, index, index + 1, scope.clone(), &mut stack);
7966                visitor.drain_cpp_work(stack, &reparsed_ancestry);
7967            });
7968            self.consumed_fragment_regions.push((start, end));
7969            recovered = true;
7970        }
7971        recovered
7972    }
7973
7974    /// Declare one Module per namespace level of `levels` under `package_name`,
7975    /// each recording the range of the construct that writes it, and return the
7976    /// innermost level's package name and Module. A level an earlier definition
7977    /// already declared is reused, which is also how a reopened namespace keeps
7978    /// the range of the definition that first declared it.
7979    fn declare_namespace_levels(
7980        &mut self,
7981        mut package_name: String,
7982        levels: Vec<(String, Range, Option<Range>)>,
7983        node: Node<'_>,
7984        ancestry: &ParentIndex<'_>,
7985    ) -> (String, Option<CodeUnit>) {
7986        let mut module = None;
7987        for (component, range, name_range) in levels {
7988            let full_name = if package_name.is_empty() {
7989                component
7990            } else {
7991                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
7992            };
7993            let level = CodeUnit::new_fq(
7994                self.file.clone(),
7995                CodeUnitType::Module,
7996                "",
7997                full_name.clone(),
7998                cpp_namespace_fq(&full_name),
7999            );
8000            if !self.parsed.contains_declaration(&level) {
8001                if let Some(name_range) = name_range {
8002                    self.add_declaration_with_name_range(
8003                        level.clone(),
8004                        range,
8005                        name_range,
8006                        Some((ancestry, node)),
8007                        None,
8008                        None,
8009                    );
8010                } else {
8011                    self.add_declaration_with_range(
8012                        ancestry,
8013                        level.clone(),
8014                        range,
8015                        Some(node),
8016                        None,
8017                        None,
8018                    );
8019                }
8020            }
8021            package_name = full_name;
8022            module = Some(level);
8023        }
8024        (package_name, module)
8025    }
8026
8027    /// [`Self::declare_namespace_levels`] for levels a recovered-namespace
8028    /// index restores. Each level carries the range of its own name token,
8029    /// which the walk's current node cannot supply: that node is whichever
8030    /// declaration the walk reached first inside the region, and deriving a
8031    /// level's name from it paired the level's range with an unrelated
8032    /// construct's name token (#4292).
8033    fn declare_recovered_namespace_levels(
8034        &mut self,
8035        mut package_name: String,
8036        levels: Vec<(String, Range, Range)>,
8037    ) -> (String, Option<CodeUnit>) {
8038        let mut module = None;
8039        for (component, range, name_range) in levels {
8040            let full_name = if package_name.is_empty() {
8041                component
8042            } else {
8043                format!("{package_name}{CPP_PACKAGE_SEPARATOR}{component}")
8044            };
8045            let level = CodeUnit::new_fq(
8046                self.file.clone(),
8047                CodeUnitType::Module,
8048                "",
8049                full_name.clone(),
8050                cpp_namespace_fq(&full_name),
8051            );
8052            if !self.parsed.contains_declaration(&level) {
8053                self.add_declaration_with_name_range(
8054                    level.clone(),
8055                    range,
8056                    name_range,
8057                    None,
8058                    None,
8059                    None,
8060                );
8061            }
8062            package_name = full_name;
8063            module = Some(level);
8064        }
8065        (package_name, module)
8066    }
8067
8068    /// Restore the complete lexical namespace path when recovery dropped an
8069    /// ancestor or retained it past its real close. Parsed namespaces beginning
8070    /// inside the recovered region still contribute their normal nesting.
8071    /// Crossing that boundary also clears any malformed class owner.
8072    fn recovered_namespace_scope(
8073        &mut self,
8074        node: Node<'_>,
8075        scope: &ScopeInfo,
8076    ) -> Option<ScopeInfo> {
8077        self.orphaned_namespaces.region_at(node.start_byte())?;
8078        // Each level carries the `namespace Name { ... }` construct that writes
8079        // it along with the level's own name token. Declaring them at `node`
8080        // instead recorded the range of whichever member the walk happened to
8081        // reach first, so a namespace whose head collapsed into an ERROR
8082        // reported its first inner declaration's lines as its own (#3309), and
8083        // deriving the name from that node paired the level's range with the
8084        // member's name token (#4292).
8085        let levels = self
8086            .orphaned_namespaces
8087            .enclosing_namespace_levels(node, self.source);
8088        let package_name = levels
8089            .iter()
8090            .map(|(component, ..)| component.as_str())
8091            .collect::<Vec<_>>()
8092            .join(CPP_PACKAGE_SEPARATOR);
8093        if package_name == scope.package_name {
8094            return None;
8095        }
8096        let (package_name, module) = self.declare_recovered_namespace_levels(String::new(), levels);
8097        Some(ScopeInfo {
8098            package_name,
8099            module,
8100            // A recovered namespace body is no longer inside the class (or
8101            // function) that tree-sitter accidentally used as its wrapper.
8102            // Keep namespace/module context, but never carry that owner into
8103            // declarations published from the recovered region.
8104            class_unit: None,
8105            template_signature: None,
8106            template_metadata: None,
8107            declarations_are_fields: false,
8108            recovered_specialization_member_scope: false,
8109            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8110            local_block: None,
8111            visible_using_declarations: scope.visible_using_declarations.clone(),
8112        })
8113    }
8114
8115    fn visit_namespace<'tree>(
8116        &mut self,
8117        node: Node<'tree>,
8118        scope: &ScopeInfo,
8119        stack: &mut Vec<CppWork<'tree>>,
8120        ancestry: &ParentIndex<'tree>,
8121    ) {
8122        let name_node = node.child_by_field_name("name");
8123        let Some(name_node) = name_node else {
8124            if let Some(body) = cpp_body_node(node) {
8125                stack.push(CppWork::Container(CppContainer {
8126                    node: body,
8127                    scope: scope.clone(),
8128                }));
8129            }
8130            return;
8131        };
8132        // Diagnostic corpora contain deliberately ill-formed global namespace
8133        // definitions such as `namespace ::outer::inner {}`. Tree-sitter keeps
8134        // the leading global `::` as the first anonymous child. Honor that AST
8135        // boundary instead of appending the name to the lexical namespace;
8136        // appending produced legacy names such as `outer::::outer::inner`, which
8137        // could not round-trip through the structured FqName boundary.
8138        let explicitly_global = name_node
8139            .child(0)
8140            .is_some_and(|child| !child.is_named() && child.kind() == "::");
8141        let components = cpp_namespace_name_components(name_node, self.source);
8142        if components.is_empty() {
8143            return;
8144        }
8145        // One Module per namespace level. C++17's `namespace a::b { ... }` is
8146        // DEFINED to mean `namespace a { namespace b { ... } }`, so the
8147        // shorthand must declare `a` as well as `a::b` -- extracting only the
8148        // innermost level left the enclosing namespace undeclared and made the
8149        // two spellings of one construct disagree (issue #1878).
8150        let package_name = if explicitly_global {
8151            String::new()
8152        } else {
8153            scope.package_name.clone()
8154        };
8155        let definition = node_range(node);
8156        // The `namespace a::b` shorthand names every level in one node: each
8157        // level's name occurrence is its own token, the same evidence the
8158        // recovered-namespace path carries per level (#4292).
8159        let mut component_ranges =
8160            cpp_namespace_name_component_ranges(name_node, self.source).into_iter();
8161        let (package_name, module) = self.declare_namespace_levels(
8162            package_name,
8163            components
8164                .into_iter()
8165                .map(|component| {
8166                    let name_range = component_ranges
8167                        .next()
8168                        .filter(|(name, _)| *name == component)
8169                        .map(|(_, range)| range);
8170                    (component, definition, name_range)
8171                })
8172                .collect(),
8173            node,
8174            ancestry,
8175        );
8176
8177        let namespace_scope = ScopeInfo {
8178            package_name,
8179            module,
8180            // C++ never nests a namespace inside a class, so a surviving
8181            // class_unit here is always recovery bleed: a malformed-region
8182            // boundary upstream mis-scoped this namespace block. Keeping the
8183            // owner would mint the namespace's declarations as class members
8184            // under a re-appended package, desyncing the fq boundary assert
8185            // (#2306). Dropping it is identity-neutral for valid code, where
8186            // class_unit is always empty at a namespace definition.
8187            class_unit: None,
8188            template_signature: scope.template_signature.clone(),
8189            template_metadata: scope.template_metadata.clone(),
8190            declarations_are_fields: false,
8191            recovered_specialization_member_scope: false,
8192            visible_using_namespaces: scope.visible_using_namespaces.clone(),
8193            local_block: None,
8194            visible_using_declarations: scope.visible_using_declarations.clone(),
8195        };
8196        let container = cpp_body_node(node).unwrap_or(node);
8197        // A malformed export-macro class body may turn the following class
8198        // into a descendant of a bogus function/labeled/error envelope. Those
8199        // descendants are not declaration containers and the ordinary walk
8200        // intentionally does not descend into them. Scan the namespace tree
8201        // once for the strict embedded class geometry before scheduling its
8202        // normal declarations. When one envelope matches, its helper recovers
8203        // every embedded class and the walk need not inspect its descendants.
8204        let mut candidates = vec![container];
8205        while let Some(candidate) = candidates.pop() {
8206            if matches!(
8207                candidate.kind(),
8208                "ERROR" | "function_definition" | "labeled_statement"
8209            ) && self.visit_embedded_function_like_export_classes(
8210                candidate,
8211                &namespace_scope,
8212                stack,
8213                ancestry,
8214            ) {
8215                continue;
8216            }
8217            for index in (0..candidate.named_child_count()).rev() {
8218                candidates.push(
8219                    candidate
8220                        .named_child(index)
8221                        .expect("index below the node's own named child count"),
8222                );
8223            }
8224        }
8225        stack.push(CppWork::Container(CppContainer {
8226            node: container,
8227            scope: namespace_scope,
8228        }));
8229    }
8230
8231    fn visit_class_like<'tree>(
8232        &mut self,
8233        node: Node<'tree>,
8234        scope: &ScopeInfo,
8235        stack: &mut Vec<CppWork<'tree>>,
8236        ancestry: &ParentIndex<'tree>,
8237    ) {
8238        let Some(name) = class_like_name(node, self.source, ancestry) else {
8239            return;
8240        };
8241        let name = qualified_class_name_chain(node, self.source, scope)
8242            .map(|chain| chain.join("$"))
8243            .unwrap_or(name);
8244        self.visit_named_class_like(node, name, scope, stack, ancestry);
8245    }
8246
8247    fn visit_named_class_like<'tree>(
8248        &mut self,
8249        node: Node<'tree>,
8250        name: String,
8251        scope: &ScopeInfo,
8252        stack: &mut Vec<CppWork<'tree>>,
8253        ancestry: &ParentIndex<'tree>,
8254    ) {
8255        let body = cpp_body_node(node);
8256        let definition_body_present = body.is_some();
8257        let raw_supertypes = matches!(node.kind(), "class_specifier" | "struct_specifier")
8258            .then(|| extract_cpp_supertypes(node, self.source));
8259        self.visit_named_class_like_shape(
8260            node,
8261            name,
8262            body,
8263            definition_body_present,
8264            None,
8265            raw_supertypes,
8266            Some(extract_cpp_base_facts(node, self.source)),
8267            scope,
8268            stack,
8269            ancestry,
8270        );
8271    }
8272
8273    /// Whether this class-like declaration is a C tag that belongs to the
8274    /// enclosing non-aggregate scope rather than to the aggregate it is
8275    /// lexically written inside.
8276    ///
8277    /// `class_specifier` is deliberately excluded: `class` is not C, so text
8278    /// that spells one in a `.c` file is not C code and keeps the C++ reading
8279    /// rather than getting a half-C identity.
8280    fn mints_tag_at_enclosing_c_scope(
8281        &self,
8282        declaration_node: Node<'_>,
8283        scope: &ScopeInfo,
8284        ancestry: &ParentIndex<'_>,
8285    ) -> bool {
8286        if scope.class_unit.is_none()
8287            || !matches!(
8288                declaration_node.kind(),
8289                "struct_specifier" | "union_specifier" | "enum_specifier"
8290            )
8291            || class_like_name(declaration_node, self.source, ancestry).is_none()
8292        {
8293            return false;
8294        }
8295        // A class-specifier ancestor proves this is C++ syntax. A C reading of
8296        // the header must not apply C tag hoisting to structs nested in that
8297        // class, because that invents a file-scope identity for its members.
8298        let mut ancestor = ancestry.parent(declaration_node);
8299        while let Some(current) = ancestor {
8300            if current.kind() == "class_specifier" {
8301                return false;
8302            }
8303            ancestor = ancestry.parent(current);
8304        }
8305        true
8306    }
8307
8308    #[allow(clippy::too_many_arguments)]
8309    fn visit_named_class_like_shape<'tree>(
8310        &mut self,
8311        declaration_node: Node<'tree>,
8312        name: String,
8313        body: Option<Node<'tree>>,
8314        definition_body_present: bool,
8315        explicit_range: Option<Range>,
8316        raw_supertypes: Option<Vec<String>>,
8317        base_facts: Option<Vec<CppBaseSpecifierFact>>,
8318        scope: &ScopeInfo,
8319        stack: &mut Vec<CppWork<'tree>>,
8320        ancestry: &ParentIndex<'tree>,
8321    ) -> CodeUnit {
8322        let displaced_macro_tail = if explicit_range.is_none() {
8323            body.and_then(|body| displaced_macro_class_tail(declaration_node, body, self.source))
8324        } else {
8325            None
8326        };
8327        let explicit_range = explicit_range.or(displaced_macro_tail.map(|tail| tail.class_range));
8328        let recovered_scope = self.scope_for_recovered_exported_class(
8329            declaration_node,
8330            &name,
8331            definition_body_present,
8332            scope,
8333            ancestry,
8334        );
8335        // C tag scope (C17 6.2.1, 6.7.2.3): a tag declared inside another
8336        // aggregate's member list is declared at the enclosing non-aggregate
8337        // scope, not nested inside the aggregate. `scope.class_unit` is the
8338        // only aggregate carrier in this walk, so dropping it puts the tag at
8339        // the nearest enclosing non-aggregate scope -- the module at file or
8340        // namespace scope, and the same block-scope representation a
8341        // function-local aggregate already gets. The tag's own body scope
8342        // below still owns its members, so fields and enumerators are
8343        // unaffected.
8344        let c_tag_scope;
8345        let c_tag_scope_applies =
8346            self.mints_tag_at_enclosing_c_scope(declaration_node, &recovered_scope, ancestry);
8347        if !self.c_tag_semantics && c_tag_scope_applies {
8348            self.c_tag_scope_witness = true;
8349        }
8350        let scope = if self.c_tag_semantics && c_tag_scope_applies {
8351            c_tag_scope = ScopeInfo {
8352                class_unit: None,
8353                ..recovered_scope.clone()
8354            };
8355            &c_tag_scope
8356        } else {
8357            &recovered_scope
8358        };
8359        let local_block = scope
8360            .local_block
8361            .as_ref()
8362            .filter(|_| scope.class_unit.is_none());
8363        let short_name = if let Some(parent) = &scope.class_unit {
8364            cpp_join_nested_short(parent.short_name(), &name)
8365        } else if let Some(block) = local_block {
8366            cpp_join_nested_short(&block.short_name, &name)
8367        } else {
8368            name.clone()
8369        };
8370        // A top-level out-of-line qualified class definition (`struct
8371        // Outer::Inner { ... }` inside its namespace, #2246) carries its
8372        // nesting chain as the `$`-joined display name; push one Type/Nested
8373        // segment per class so segment-pop owner navigation keeps working.
8374        // Every other leaf name stays opaque so a literal `$` in a source
8375        // identifier never crosses the split/join boundary (#2140).
8376        let qualified_chain = if scope.class_unit.is_none() && local_block.is_none() {
8377            qualified_class_name_chain(declaration_node, self.source, scope)
8378                .filter(|chain| chain.join("$") == name)
8379        } else {
8380            None
8381        };
8382        let fq = if let Some(block) = local_block {
8383            block
8384                .fq
8385                .clone()
8386                .with_pushed(segment_interner().intern(&name, SegmentKind::Nested))
8387        } else if let Some(chain) = qualified_chain {
8388            let mut fq = FqName::new();
8389            cpp_push_package(&mut fq, &scope.package_name);
8390            let mut first = true;
8391            for component in chain {
8392                let kind = if first {
8393                    SegmentKind::Type
8394                } else {
8395                    SegmentKind::Nested
8396                };
8397                fq.push(segment_interner().intern(&component, kind));
8398                first = false;
8399            }
8400            fq
8401        } else {
8402            cpp_leaf_fq(
8403                &scope.package_name,
8404                scope.class_unit.as_ref(),
8405                &name,
8406                SegmentKind::Nested,
8407                SegmentKind::Type,
8408            )
8409        };
8410        let code_unit = CodeUnit::with_signature_and_fq(
8411            self.file.clone(),
8412            CodeUnitType::Class,
8413            scope.package_name.clone(),
8414            short_name,
8415            scope.template_signature.clone(),
8416            false,
8417            fq,
8418        );
8419        let has_body = definition_body_present;
8420        if !has_body && self.parsed.contains_declaration(&code_unit) {
8421            if let Some(range) = explicit_range {
8422                self.link_source_range(ancestry, &code_unit, range, Some(declaration_node));
8423            } else {
8424                self.link_source_declaration(ancestry, &code_unit, declaration_node);
8425            }
8426            self.parsed.record_navigation_range(
8427                code_unit.clone(),
8428                explicit_range.unwrap_or_else(|| cpp_declaration_range(declaration_node)),
8429            );
8430            return code_unit;
8431        }
8432        if has_body {
8433            // A forward declaration above this definition is one of the
8434            // class's declaration sites: C++ injects the class name at the
8435            // forward declaration, so a reference between the two spells this
8436            // class (#3297). Moving the unit to its body must not drop the
8437            // earlier site, exactly as a function prototype and its body stay
8438            // two ranges of one declaration (#1650).
8439            let earlier_ranges = self.parsed.declaration_ranges(&code_unit).to_vec();
8440            if let Some(range) = explicit_range {
8441                self.replace_declaration_with_range_deferred(
8442                    ancestry,
8443                    code_unit.clone(),
8444                    range,
8445                    Some(declaration_node),
8446                    None,
8447                    None,
8448                );
8449            } else {
8450                self.replace_declaration_deferred(
8451                    ancestry,
8452                    code_unit.clone(),
8453                    declaration_node,
8454                    None,
8455                    None,
8456                );
8457            }
8458            for range in earlier_ranges {
8459                self.parsed.add_declaration_range(&code_unit, range);
8460            }
8461        } else {
8462            self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
8463        }
8464        if let Some(declaration) = self
8465            .source_links
8466            .get(&code_unit)
8467            .and_then(|links| links.last())
8468            .copied()
8469        {
8470            let mut properties = self.source_properties.borrow_mut();
8471            let fact = properties
8472                .get_mut(&declaration)
8473                .expect("linked class has source properties");
8474            fact.occurrence_role = if has_body {
8475                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition
8476            } else {
8477                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::DeclarationOnly
8478            };
8479            fact.class_strength = if has_body {
8480                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full
8481            } else {
8482                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Forward
8483            };
8484            if let Some(base_facts) = base_facts.as_ref() {
8485                fact.bases = base_facts.clone();
8486            }
8487        }
8488        if let Some(raw_supertypes) = raw_supertypes {
8489            self.parsed
8490                .set_raw_supertypes(code_unit.clone(), raw_supertypes);
8491        }
8492        self.parsed.add_signature(
8493            code_unit.clone(),
8494            render_cpp_type_signature(
8495                declaration_node,
8496                self.source,
8497                scope.template_signature.as_deref(),
8498            ),
8499        );
8500        if let Some(metadata) = &scope.template_metadata {
8501            let primary_short_name = if let Some(parent) = &scope.class_unit {
8502                cpp_join_nested_short(parent.short_name(), &metadata.primary_name)
8503            } else {
8504                metadata.primary_name.clone()
8505            };
8506            let primary_fq_name = CodeUnit::new(
8507                self.file.clone(),
8508                CodeUnitType::Class,
8509                scope.package_name.clone(),
8510                primary_short_name,
8511            )
8512            .fq_name();
8513            let mut metadata = metadata.clone();
8514            metadata.primary_fq_name = primary_fq_name;
8515            self.parsed
8516                .set_cpp_template_metadata(code_unit.clone(), metadata);
8517        }
8518        if let Some(parent) = &scope.class_unit {
8519            self.parsed.add_child(parent.clone(), code_unit.clone());
8520        } else if let Some(block) = local_block {
8521            self.parsed
8522                .add_child(block.callable.clone(), code_unit.clone());
8523        } else if let Some(module) = &scope.module {
8524            self.parsed.add_child(module.clone(), code_unit.clone());
8525        }
8526
8527        if let Some(body) = body {
8528            let mut nested_scope = scope.clone();
8529            nested_scope.class_unit = Some(code_unit.clone());
8530            nested_scope.local_block = None;
8531            nested_scope.template_signature = scope.template_signature.clone();
8532            // Template metadata describes the class just created. It must not
8533            // leak into ordinary nested declarations in that class's body.
8534            // Recovered export-macro specializations carry a separate scope bit
8535            // for their declaration-shaped body members.
8536            nested_scope.template_metadata = None;
8537            // Export-macro class bodies recovered from a function_definition use
8538            // compound_statement children, whose direct fields are declarations.
8539            nested_scope.recovered_specialization_member_scope =
8540                scope.template_metadata.as_ref().is_some_and(|metadata| {
8541                    declaration_node.kind() == "function_definition" && metadata.is_specialization()
8542                });
8543            nested_scope.declarations_are_fields =
8544                is_recovered_exported_class_container(declaration_node, self.source)
8545                    || nested_scope.recovered_specialization_member_scope;
8546            if let Some(displaced) = displaced_macro_tail {
8547                // A macro-shaped field without a source semicolon can make
8548                // tree-sitter consume the real class terminator as an ERROR
8549                // inside that field, then retain following namespace items as
8550                // later field-list children. Drain the proven class prefix
8551                // first and re-own only the structured tail with the outer
8552                // scope. The tail is pushed first because the work stack is
8553                // LIFO.
8554                push_cpp_sibling_range(
8555                    body,
8556                    displaced.split_index,
8557                    usize::MAX,
8558                    scope.clone(),
8559                    stack,
8560                );
8561                push_cpp_sibling_range(body, 0, displaced.split_index, nested_scope, stack);
8562            } else {
8563                stack.push(CppWork::Container(CppContainer {
8564                    node: body,
8565                    scope: nested_scope,
8566                }));
8567            }
8568        }
8569        if declaration_node.kind() == "enum_specifier" {
8570            self.visit_enum_enumerators(ancestry, declaration_node, scope, &code_unit);
8571        }
8572        code_unit
8573    }
8574
8575    fn visit_enum_enumerators(
8576        &mut self,
8577        ancestry: &ParentIndex<'_>,
8578        node: Node<'_>,
8579        scope: &ScopeInfo,
8580        parent: &CodeUnit,
8581    ) {
8582        walk_named_tree_preorder(node, false, |child| {
8583            if child.kind() != "enumerator" {
8584                return WalkControl::Continue;
8585            }
8586            let Some(name_node) = child.child_by_field_name("name") else {
8587                return WalkControl::Continue;
8588            };
8589            let name = normalize_cpp_whitespace(node_text(name_node, self.source));
8590            if name.is_empty() {
8591                return WalkControl::Continue;
8592            }
8593            let signature = normalize_cpp_whitespace(node_text(child, self.source));
8594            self.visit_enum_enumerator(ancestry, child, scope, parent, &name, &signature);
8595            WalkControl::Continue
8596        });
8597    }
8598
8599    fn visit_enum_enumerator(
8600        &mut self,
8601        ancestry: &ParentIndex<'_>,
8602        declaration_node: Node<'_>,
8603        scope: &ScopeInfo,
8604        parent: &CodeUnit,
8605        name: &str,
8606        signature: &str,
8607    ) {
8608        let code_unit = CodeUnit::new_fq(
8609            self.file.clone(),
8610            CodeUnitType::Field,
8611            scope.package_name.clone(),
8612            cpp_join_member_short(parent.short_name(), name),
8613            parent
8614                .fq()
8615                .clone()
8616                .with_pushed(segment_interner().intern(name, SegmentKind::Member)),
8617        );
8618        if self.parsed.contains_declaration(&code_unit) {
8619            return;
8620        }
8621        self.add_declaration(
8622            ancestry,
8623            code_unit.clone(),
8624            declaration_node,
8625            Some(parent.clone()),
8626            None,
8627        );
8628        self.parsed.add_signature(code_unit, signature.to_string());
8629    }
8630
8631    fn visit_function_definition<'tree>(
8632        &mut self,
8633        node: Node<'tree>,
8634        scope: &ScopeInfo,
8635        stack: &mut Vec<CppWork<'tree>>,
8636        ancestry: &ParentIndex<'tree>,
8637    ) {
8638        // An attribute-like macro invocation whose argument is a declaration can
8639        // swallow every declaration written after it into one bogus
8640        // `function_definition` (#2551). This owns the whole region, so it runs
8641        // ahead of `visit_macro_swallowed_function_declarations` below, which
8642        // admits the same envelope by its head macro token and reads single
8643        // declarators out of nodes this recovery reparses properly.
8644        if self.visit_collapsed_macro_declaration_run(node, scope, ancestry) {
8645            return;
8646        }
8647        // A file-scope object-like macro sentinel the parser cannot see (issue
8648        // #941, e.g. `BEGIN_NS`/`END_NS`) makes tree-sitter recover the region it
8649        // prefixes as a bogus `function_definition` that swallows real namespaces,
8650        // classes, and members. Reparse the swallowed interior as C++ items so the
8651        // ordinary declaration visitors index it with byte/line-exact ownership.
8652        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
8653            return;
8654        }
8655        // The omitted-semicolon field near miss (`DWORD preserved_field` ahead
8656        // of `void PreserveFollowingFunction() {}`): the macro-declaration
8657        // grammar folds the pair into the function's head as two leading
8658        // macro_modifiers where the older grammar left a damaged
8659        // field_declaration. Index the field; the function itself continues
8660        // through the ordinary path below.
8661        if let Some((type_modifier, name_modifier)) = omitted_semicolon_macro_field_pair(node) {
8662            self.visit_omitted_semicolon_macro_field(
8663                node,
8664                type_modifier,
8665                name_modifier,
8666                scope,
8667                ancestry,
8668            );
8669        }
8670        // The macro-return stand-in (`CUTLASS_DEVICE static OperandLayout
8671        // Layout() { ... }`): the same clean envelope, but the return type
8672        // names an active template type parameter, and the older grammar's
8673        // phantom field was the definition `OperandLayout::packed` resolved to
8674        // (#1129). Recover that field from the clean head.
8675        if scope.class_unit.is_some()
8676            && let Some((head_start, type_node)) =
8677                macro_return_template_parameter_field(node, self.source, ancestry)
8678        {
8679            self.visit_macro_return_template_parameter_field(
8680                node, head_start, type_node, scope, ancestry,
8681            );
8682        }
8683        if node.has_error() {
8684            self.visit_macro_swallowed_function_declarations(node, scope);
8685        }
8686        if let Some((class_node, name, raw_supertypes, base_facts)) =
8687            recover_exported_class_function_definition(node, self.source)
8688        {
8689            if let Some(body) = cpp_body_node(node)
8690                && let Some(close) = self
8691                    .orphaned_namespaces
8692                    .matching_close_brace(body.start_byte())
8693                && close.end_byte < body.end_byte()
8694            {
8695                let class_range = Range {
8696                    start_byte: node.start_byte(),
8697                    end_byte: close.end_byte,
8698                    start_line: node.start_position().row + 1,
8699                    end_line: close.end_line,
8700                };
8701                // Parse the body in class context, preserving the original
8702                // keyword, recovered name and body as included ranges. Parsing
8703                // the interior at file scope turns constructors and access
8704                // labels into another oversized ERROR instead of members.
8705                let mut head = vec![node];
8706                let mut keyword = None;
8707                let mut name_node = None;
8708                while let Some(part) = head.pop() {
8709                    if part.start_byte() >= body.start_byte() || part.is_missing() {
8710                        continue;
8711                    }
8712                    if matches!(part.kind(), "class" | "struct" | "union") {
8713                        keyword = Some(part);
8714                    }
8715                    if matches!(part.kind(), "identifier" | "type_identifier")
8716                        && node_text(part, self.source) == name
8717                    {
8718                        name_node = Some(part);
8719                    }
8720                    let mut cursor = part.walk();
8721                    head.extend(part.children(&mut cursor));
8722                }
8723                if let (Some(keyword), Some(name_node)) = (keyword, name_node)
8724                    && let Some(type_name) = keyword
8725                        .parent()
8726                        .and_then(|parent| parent.child_by_field_name("name"))
8727                    && let Some(tree) = parse_source_ranges_with_cancellation(
8728                        &tree_sitter_cpp::LANGUAGE.into(),
8729                        self.source,
8730                        &[
8731                            (keyword.start_byte(), type_name.start_byte()),
8732                            (name_node.start_byte(), name_node.end_byte()),
8733                            (body.start_byte(), close.end_byte),
8734                        ],
8735                        None,
8736                    )
8737                    && let Some(reparsed_class) = tree.root_node().named_child(0)
8738                    && let Some(class_body) = cpp_body_node(reparsed_class)
8739                    && class_body.start_byte() == body.start_byte()
8740                    && class_body.end_byte() == close.end_byte
8741                    && let Some(tail) =
8742                        cpp_reparse_region_items(self.source, close.end_byte, node.end_byte())
8743                {
8744                    let class_body_range = class_body.byte_range();
8745                    self.with_recovery_source_context(node, |visitor| {
8746                        let source_context = visitor.active_source_context.clone();
8747                        let class_unit = visitor.visit_named_class_like_shape(
8748                            class_node,
8749                            name,
8750                            None,
8751                            true,
8752                            Some(class_range),
8753                            raw_supertypes,
8754                            base_facts,
8755                            scope,
8756                            stack,
8757                            ancestry,
8758                        );
8759                        visitor.parsed.record_materialization(
8760                            MaterializationRecord::RecoveredDeclaration {
8761                                recovery: class_range,
8762                                unit: class_unit.clone(),
8763                            },
8764                        );
8765                        let member_scope = ScopeInfo {
8766                            package_name: class_unit.package_name().to_string(),
8767                            class_unit: Some(class_unit),
8768                            declarations_are_fields: true,
8769                            template_metadata: None,
8770                            recovered_specialization_member_scope: false,
8771                            ..scope.clone()
8772                        };
8773                        let tail_range = tail.root_node().byte_range();
8774                        let class_body_recovery = cpp_recovery_window(
8775                            visitor.source,
8776                            class_body_range.start,
8777                            class_body_range.end,
8778                        );
8779                        let tail_recovery =
8780                            cpp_recovery_window(visitor.source, tail_range.start, tail_range.end);
8781                        visitor.partitioned_regions.push(CppPartitionedRegion {
8782                            tree: tail,
8783                            range: tail_range,
8784                            scope: scope.clone(),
8785                            recovery: tail_recovery,
8786                            source_context: source_context.clone(),
8787                        });
8788                        visitor.partitioned_regions.push(CppPartitionedRegion {
8789                            tree,
8790                            range: class_body_range,
8791                            scope: member_scope,
8792                            recovery: class_body_recovery,
8793                            source_context: source_context.clone(),
8794                        });
8795                    });
8796                    return;
8797                }
8798            }
8799            let body = cpp_body_node(class_node);
8800            let displaced_namespace = cpp_body_node(node)
8801                .and_then(|_| displaced_export_function_namespace_shape(node, self.source));
8802            let fragmented = cpp_body_node(node).and_then(|body| {
8803                fragmented_export_function_body_region(
8804                    node,
8805                    body,
8806                    self.source,
8807                    displaced_namespace.as_ref(),
8808                )
8809            });
8810            // The recovery tuple's first node is the class-like type when the
8811            // parser exposes one, but the synthetic wrapper owns the compound
8812            // statement that contains the truncated class body. Use the
8813            // wrapper body for fragmented-member detection; retain the
8814            // class-node body for the ordinary (non-fragmented) path below.
8815            if let Some(fragmented) = fragmented {
8816                // The lifted sibling no longer sits below the parser-visible
8817                // namespace node. Restore the current parent scope when the
8818                // ordinary work walk reaches that class.
8819                if let Some(boundary) =
8820                    fragmented_export_sibling_class_boundary(node, self.source, ancestry)
8821                        .filter(|boundary| boundary.start_byte() == fragmented.reparse_end)
8822                {
8823                    let mut boundary_scope = scope.clone();
8824                    for sibling in cpp_following_named_siblings(node, self.source, ancestry) {
8825                        if sibling.start_byte() >= boundary.start_byte() {
8826                            break;
8827                        }
8828                        if let Some(namespace) = cpp_using_namespace_target(sibling, self.source) {
8829                            boundary_scope.visible_using_namespaces.push(namespace);
8830                        }
8831                        push_cpp_using_declaration_binding(
8832                            &mut boundary_scope,
8833                            sibling,
8834                            self.source,
8835                        );
8836                    }
8837                    self.recovered_class_sibling_scopes
8838                        .insert(boundary.id(), boundary_scope);
8839                }
8840                let mut recovered_constructor = None;
8841                let mut recovered_prefix_tree = None;
8842                let outcome = match self.reparse_fragmented_export_class_members(&fragmented, &name)
8843                {
8844                    Some(FragmentedExportMembers::Complete(tree)) => {
8845                        if let Some(body) = body
8846                            && let Some(range) =
8847                                cpp_reparsed_synthetic_initializer_constructor_range(
8848                                    tree.root_node(),
8849                                    &name,
8850                                    self.source,
8851                                    body.end_byte(),
8852                                )
8853                        {
8854                            recovered_constructor = Some(range);
8855                            recovered_prefix_tree = Some(tree);
8856                            None
8857                        } else {
8858                            Some(FragmentedExportMembers::Complete(tree))
8859                        }
8860                    }
8861                    outcome => outcome,
8862                };
8863                let mut class_stack = Vec::new();
8864                let class_unit = self.visit_named_class_like_shape(
8865                    class_node,
8866                    name,
8867                    None,
8868                    true,
8869                    Some(fragmented.class_range),
8870                    raw_supertypes,
8871                    base_facts.clone(),
8872                    scope,
8873                    &mut class_stack,
8874                    ancestry,
8875                );
8876                self.parsed
8877                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
8878                        recovery: fragmented.class_range,
8879                        unit: class_unit.clone(),
8880                    });
8881                let complete = outcome.is_some_and(|outcome| {
8882                    self.visit_fragmented_export_class_members(
8883                        node,
8884                        outcome,
8885                        class_unit.clone(),
8886                        scope,
8887                    )
8888                });
8889                if complete {
8890                    self.consumed_fragment_regions
8891                        .push((node.start_byte(), fragmented.class_range.end_byte));
8892                } else {
8893                    // The reparse can fail when the first constructor or a
8894                    // method body is split into statement-shaped siblings.
8895                    // Keep the recovered class envelope, but do not visit the
8896                    // synthetic wrapper body: its initializer expressions can
8897                    // look like same-named member functions (for example
8898                    // `Token.location(loc)`). Re-own only the original sibling
8899                    // nodes that fall inside the proven class range. Their CST
8900                    // shapes retain the real field/function kinds and ranges.
8901                    let member_scope = ScopeInfo {
8902                        package_name: class_unit.package_name().to_string(),
8903                        module: scope.module.clone(),
8904                        class_unit: Some(class_unit.clone()),
8905                        template_signature: scope.template_signature.clone(),
8906                        template_metadata: None,
8907                        declarations_are_fields: true,
8908                        recovered_specialization_member_scope: false,
8909                        visible_using_namespaces: scope.visible_using_namespaces.clone(),
8910                        local_block: None,
8911                        visible_using_declarations: scope.visible_using_declarations.clone(),
8912                    };
8913                    for candidate in cpp_following_named_siblings(node, self.source, ancestry) {
8914                        if candidate.start_byte() >= fragmented.reparse_end {
8915                            break;
8916                        }
8917                        if cpp_fragment_sibling_is_class_member(
8918                            candidate,
8919                            fragmented.reparse_end,
8920                            self.source,
8921                        ) {
8922                            self.recovered_class_sibling_scopes
8923                                .insert(candidate.id(), member_scope.clone());
8924                        }
8925                    }
8926                    if let Some(range) = recovered_constructor
8927                        && let (Some(prefix_tree), Some(body)) = (recovered_prefix_tree, body)
8928                    {
8929                        let prefix_root = prefix_tree.root_node();
8930                        let prefix_ancestry = self.recovery_ancestry(prefix_root);
8931                        self.visit_recovered_fragment_prefix_members(
8932                            node,
8933                            prefix_root,
8934                            range.start,
8935                            &class_unit,
8936                            scope,
8937                            &prefix_ancestry,
8938                        );
8939                        self.visit_recovered_fragment_constructor(
8940                            range,
8941                            body,
8942                            class_node,
8943                            &class_unit,
8944                            scope,
8945                            ancestry,
8946                        );
8947                    }
8948                }
8949                if let Some(boundary) = displaced_namespace {
8950                    for item in boundary.namespace_items {
8951                        self.recovered_class_sibling_scopes
8952                            .insert(item.id(), scope.clone());
8953                    }
8954                }
8955                stack.extend(class_stack);
8956                return;
8957            }
8958            let mut stack = Vec::new();
8959            let class_unit = self.visit_named_class_like_shape(
8960                class_node,
8961                name,
8962                body,
8963                body.is_some(),
8964                None,
8965                raw_supertypes,
8966                base_facts,
8967                scope,
8968                &mut stack,
8969                ancestry,
8970            );
8971            self.parsed
8972                .record_materialization(MaterializationRecord::RecoveredDeclaration {
8973                    recovery: cpp_declaration_range(node),
8974                    unit: class_unit,
8975                });
8976            // Issue #1524: the bogus `function_definition` body can run past
8977            // the class's true closing brace (the parse ends it with a
8978            // zero-width `MISSING "}"`), swallowing following namespace-scope
8979            // siblings -- they would index as members of the recovered class.
8980            // When the body's text-balanced close lands before the body's own
8981            // end, re-own the swallowed tail with the outer scope instead.
8982            if let Some(body) = body
8983                && let Some(class_close) = self
8984                    .orphaned_namespaces
8985                    .matching_close_brace(body.start_byte())
8986                && class_close.start_byte < body.end_byte()
8987            {
8988                let split = {
8989                    let mut cursor = body.walk();
8990                    body.named_children(&mut cursor)
8991                        .position(|child| child.start_byte() > class_close.start_byte)
8992                };
8993                if let Some(split) = split {
8994                    // The seeded work is a single Container over the whole
8995                    // body with the class scope; replace it with the bounded
8996                    // head (class scope) plus the swallowed tail (outer
8997                    // scope). Push tail first so the head drains first.
8998                    let seeded = stack.pop();
8999                    match seeded {
9000                        Some(CppWork::Container(container)) => {
9001                            push_cpp_sibling_range(
9002                                body,
9003                                split,
9004                                usize::MAX,
9005                                scope.clone(),
9006                                &mut stack,
9007                            );
9008                            push_cpp_sibling_range(body, 0, split, container.scope, &mut stack);
9009                        }
9010                        // visit_named_class_like_shape always seeds exactly
9011                        // one Container when a body is present.
9012                        _ => unreachable!("exported-class seed is always one Container"),
9013                    }
9014                }
9015            }
9016            while let Some(work) = stack.pop() {
9017                match work {
9018                    CppWork::Container(container) => {
9019                        push_cpp_container_work(container.node, container.scope, &mut stack);
9020                    }
9021                    CppWork::Siblings(siblings) => {
9022                        advance_cpp_siblings(siblings, self.source, &mut stack);
9023                    }
9024                    CppWork::Node(work) => {
9025                        self.visit_node(work.node, &work.scope, &mut stack, ancestry)
9026                    }
9027                }
9028            }
9029            return;
9030        }
9031        let recovered_constraint_constructor =
9032            cpp_recovered_template_macro_constructor(node, self.source);
9033        // A constraint-macro template head the parser split off keeps the
9034        // function's declared type parameter, so the member stays a template.
9035        let constrained_scope;
9036        let scope = match cpp_recovered_template_macro_function_parameter(node, self.source) {
9037            Some(parameter) => {
9038                constrained_scope = ScopeInfo {
9039                    template_signature: Some(format!(
9040                        "<{}>",
9041                        normalize_cpp_whitespace(node_text(parameter, self.source))
9042                    )),
9043                    ..scope.clone()
9044                };
9045                &constrained_scope
9046            }
9047            None => scope,
9048        };
9049        let declarator = recovered_constraint_constructor
9050            .map(|(declarator, _)| declarator)
9051            .or_else(|| {
9052                scope.class_unit.as_ref().and_then(|class| {
9053                    recovered_macro_decorated_constructor_declarator(
9054                        node,
9055                        class.identifier(),
9056                        self.source,
9057                    )
9058                })
9059            })
9060            .or_else(|| node.child_by_field_name("declarator"));
9061        let Some(declarator) = declarator else {
9062            self.visit_malformed_function_definition_container(node, scope, stack);
9063            return;
9064        };
9065        let Some(function_declarator) = extract_function_declarator(declarator) else {
9066            self.visit_malformed_function_definition_container(node, scope, stack);
9067            return;
9068        };
9069        let function = if let Some((_, callable_name)) =
9070            cpp_macro_displaced_callable_parts(function_declarator, self.source, ancestry)
9071        {
9072            extract_function_info_from_name(function_declarator, callable_name, self.source, scope)
9073        } else {
9074            extract_function_info(function_declarator, self.source, scope)
9075        };
9076        let Some(mut function) = function else {
9077            self.visit_malformed_function_definition_container(node, scope, stack);
9078            return;
9079        };
9080        if let Some((_, template_parameter)) = recovered_constraint_constructor {
9081            function.signature = format!(
9082                "template <{}>{}",
9083                normalize_cpp_whitespace(node_text(template_parameter, self.source)),
9084                function.signature
9085            );
9086        }
9087        let code_unit = function.code_unit(self.file.clone());
9088        // Keep an earlier same-file prototype as another physical occurrence
9089        // of this callable. `CodeUnit` already identifies the role-neutral
9090        // overload, while ranges and signature metadata describe its
9091        // declaration/definition occurrences.
9092        self.add_declaration(ancestry, code_unit.clone(), node, None, None);
9093        let signature = if recovered_constraint_constructor.is_some() {
9094            normalize_cpp_whitespace(node_text(function_declarator, self.source))
9095        } else {
9096            render_cpp_function_display_signature_from_node(
9097                node,
9098                self.source,
9099                scope.template_signature.as_deref(),
9100                true,
9101                ancestry,
9102            )
9103        };
9104        self.add_callable_signature_with_metadata(
9105            code_unit.clone(),
9106            node.start_byte(),
9107            function_declarator,
9108            ancestry,
9109            cpp_signature_metadata(
9110                signature,
9111                function_declarator,
9112                cpp_callable_linkage(node, self.source, ancestry),
9113                self.source,
9114                ancestry,
9115            )
9116            .with_declaration_only(false),
9117        );
9118        if let Some(parent) = &scope.class_unit {
9119            self.parsed.add_child(parent.clone(), code_unit.clone());
9120        } else if let Some(module) = &scope.module {
9121            self.parsed.add_child(module.clone(), code_unit.clone());
9122        }
9123        self.visit_local_types_in_function(node, &code_unit, scope, stack, ancestry);
9124    }
9125
9126    /// Recover the namespace lost when tree-sitter promotes an export-macro
9127    /// class definition to a root-level `function_definition`.  Only a
9128    /// body-bearing, top-level recovery may borrow a namespace, and only when
9129    /// one earlier namespace-scope forward declaration proves the identity.
9130    fn scope_for_recovered_exported_class<'tree>(
9131        &mut self,
9132        node: Node<'tree>,
9133        name: &str,
9134        definition_body_present: bool,
9135        scope: &ScopeInfo,
9136        ancestry: &ParentIndex<'tree>,
9137    ) -> ScopeInfo {
9138        if !definition_body_present
9139            || !scope.package_name.is_empty()
9140            || scope.class_unit.is_some()
9141            || !(is_recovered_exported_class_container(node, self.source)
9142                || recover_function_like_export_class_pair(node, self.source).is_some()
9143                || recover_embedded_function_like_export_classes(node, self.source)
9144                    .iter()
9145                    .any(|recovered| recovered.name == name)
9146                || matches!(node.kind(), "declaration" | "field_declaration")
9147                    && recover_exported_class_declaration(node, self.source).is_some()
9148                || matches!(
9149                    node.kind(),
9150                    "class_specifier" | "struct_specifier" | "union_specifier"
9151                ) && (node.child_by_field_name("name").is_some_and(|name_node| {
9152                    cpp_export_macro_token(&normalize_cpp_whitespace(node_text(
9153                        name_node,
9154                        self.source,
9155                    )))
9156                }) || ancestry.parent(node).is_some_and(|parent| {
9157                    matches!(parent.kind(), "declaration" | "field_declaration")
9158                        && recover_exported_class_declaration(parent, self.source).is_some()
9159                        || is_recovered_exported_class_container(parent, self.source)
9160                })) && class_like_name(node, self.source, ancestry).as_deref() == Some(name))
9161        {
9162            return scope.clone();
9163        }
9164        let borrowed_namespace = self.unique_earlier_namespace_forward(node, name, ancestry);
9165        let Some(package_name) = borrowed_namespace
9166            .or_else(|| lifted_function_like_export_class_namespace(node, self.source, ancestry))
9167        else {
9168            return scope.clone();
9169        };
9170
9171        let module = CodeUnit::new_fq(
9172            self.file.clone(),
9173            CodeUnitType::Module,
9174            "",
9175            package_name.clone(),
9176            cpp_namespace_fq(&package_name),
9177        );
9178        let mut recovered = scope.clone();
9179        recovered.package_name = package_name;
9180        recovered.module = Some(module);
9181        recovered
9182    }
9183
9184    /// The unique namespace-scope forward declaration of `name` that precedes
9185    /// `recovered_node`, answered from the walk's carried-forward scan of the
9186    /// tree `recovered_node` belongs to.
9187    ///
9188    /// The scan is built on the first question and advanced by each later one,
9189    /// so a file that never reaches this path -- almost every file -- pays
9190    /// nothing, and one that reaches it thousands of times pays a single pass
9191    /// (#2754).
9192    fn unique_earlier_namespace_forward<'tree>(
9193        &mut self,
9194        recovered_node: Node<'tree>,
9195        name: &str,
9196        ancestry: &ParentIndex<'tree>,
9197    ) -> Option<String> {
9198        let mut root = recovered_node;
9199        while let Some(parent) = ancestry.parent(root) {
9200            root = parent;
9201        }
9202        let source = self.source;
9203        let scan = self
9204            .namespace_forward_scans
9205            .entry(CppTreeIdentity::of(root))
9206            .or_default();
9207        scan.advance_to(root, recovered_node.start_byte(), source, ancestry);
9208        let borrowed = scan.unique_earlier_forward(name, recovered_node);
9209
9210        #[cfg(debug_assertions)]
9211        assert_eq!(
9212            borrowed,
9213            unique_earlier_cpp_namespace_forward(recovered_node, name, source, ancestry),
9214            "the carried-forward namespace scan must answer what a fresh prefix scan answers \
9215             for {name} at byte {}",
9216            recovered_node.start_byte()
9217        );
9218
9219        borrowed
9220    }
9221
9222    fn visit_malformed_function_definition_container<'tree>(
9223        &mut self,
9224        node: Node<'tree>,
9225        scope: &ScopeInfo,
9226        stack: &mut Vec<CppWork<'tree>>,
9227    ) {
9228        let Some(body) = cpp_body_node(node) else {
9229            return;
9230        };
9231        if !cpp_contains_namespace_definition(body) {
9232            return;
9233        }
9234        stack.push(CppWork::Container(CppContainer {
9235            node: body,
9236            scope: scope.clone(),
9237        }));
9238    }
9239
9240    /// Recover the declarations swallowed by a bare begin/end macro-sentinel pair
9241    /// (issue #941). When `node` is the bogus `function_definition` tree-sitter
9242    /// emits for a sentinel-prefixed region, reparse the interior after the
9243    /// sentinel identifier as real C++ items -- confined to the region so
9244    /// every reparsed node keeps its original byte/line position -- and run the
9245    /// ordinary container visitation over the result. Returns `true` when it fired
9246    /// (the caller must then skip normal function processing). Nested sentinel
9247    /// regions recover recursively: the reparsed interior is walked through the
9248    /// same `visit_function_definition` path, so a sentinel inside the region hits
9249    /// this recovery again.
9250    /// Runs `reparse_walk` and records every declaration it mints as a
9251    /// [`MaterializationRecord::RecoveredDeclaration`] interpreting
9252    /// `recovery` (issue #1657). A reparsed sentinel region has no single
9253    /// recovered envelope unit: the ordinary visitors mint namespaces,
9254    /// classes, and members directly from the reparsed tree, so the walk's
9255    /// declaration delta is the recovered set. Records are ordered by
9256    /// declaration start byte so the parse product stays deterministic.
9257    fn record_recovered_declarations(
9258        &mut self,
9259        recovery: Range,
9260        origin: Node<'_>,
9261        reparse_walk: impl FnOnce(&mut Self),
9262    ) {
9263        // The recovered declarations begin at the recovery start, which can lie
9264        // past a branch directive the malformed origin swallowed (#6325:
9265        // `END_NS #else BEGIN_NS class C {};` parses as one declaration inside
9266        // the `#if` branch). Read their preprocessor context there.
9267        let preprocessor_anchor = (origin.start_byte() <= recovery.start_byte
9268            && recovery.start_byte < origin.end_byte())
9269        .then(|| origin.descendant_for_byte_range(recovery.start_byte, recovery.start_byte))
9270        .flatten()
9271        .unwrap_or(origin);
9272        self.with_recovery_source_context_at(origin, preprocessor_anchor, |visitor| {
9273            let source_context = visitor.active_source_context.clone();
9274            visitor.record_recovered_declarations_with_context(
9275                recovery,
9276                source_context,
9277                reparse_walk,
9278            );
9279        });
9280    }
9281
9282    /// Run a recovered walk with an owned source context and record every
9283    /// declaration it mints. Deferred partitioned regions cannot retain their
9284    /// origin `Node`, so they capture the context while the origin tree is
9285    /// live and use this same materialization path when drained.
9286    fn record_recovered_declarations_with_context(
9287        &mut self,
9288        recovery: Range,
9289        source_context: Option<CppActiveSourceContext>,
9290        reparse_walk: impl FnOnce(&mut Self),
9291    ) {
9292        // The set difference this used to be, kept as the oracle every answer
9293        // is asserted against (#2787).
9294        #[cfg(any(debug_assertions, test))]
9295        let before = self.parsed.declarations().clone();
9296
9297        self.recovery_captures.push(CppRecoveryCapture::default());
9298        let previous = self.active_source_context.take();
9299        self.active_source_context = source_context;
9300        reparse_walk(self);
9301        self.active_source_context = previous;
9302        let captured = self
9303            .recovery_captures
9304            .pop()
9305            .expect("the capture this call pushed is the one it pops");
9306
9307        // The capture holds every declaration created while it was open, once
9308        // each and in creation order, so the recovered set costs what the
9309        // recovery made rather than everything the file has declared so far.
9310        // One filter is left to apply: a created declaration that a later
9311        // deferred replacement removed is not in the parse product to report.
9312        let mut minted: Vec<CodeUnit> = captured
9313            .created
9314            .into_iter()
9315            .filter(|unit| self.parsed.contains_declaration(unit))
9316            .collect();
9317        minted.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9318
9319        #[cfg(any(debug_assertions, test))]
9320        {
9321            let mut rediscovered: Vec<CodeUnit> = self
9322                .parsed
9323                .declarations()
9324                .iter()
9325                .filter(|unit| !before.contains(*unit))
9326                .cloned()
9327                .collect();
9328            rediscovered.sort_by_cached_key(|unit| self.recovered_declaration_order(unit));
9329            assert_eq!(
9330                minted, rediscovered,
9331                "the captured recovered set must be the declaration delta of the reparse \
9332                 walk over {recovery:?}"
9333            );
9334        }
9335
9336        for unit in minted {
9337            self.parsed
9338                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9339                    recovery,
9340                    unit,
9341                });
9342        }
9343    }
9344
9345    /// Where one recovered declaration sorts: by start byte, then by name, so
9346    /// the parse product stays deterministic.
9347    fn recovered_declaration_order(&self, unit: &CodeUnit) -> (usize, String) {
9348        let start = self
9349            .parsed
9350            .declaration_ranges(unit)
9351            .first()
9352            .map(|range| range.start_byte)
9353            .unwrap_or(usize::MAX);
9354        (start, unit.fq_name().to_string())
9355    }
9356
9357    fn visit_sentinel_macro_region<'tree>(
9358        &mut self,
9359        node: Node<'tree>,
9360        scope: &ScopeInfo,
9361        stack: &mut Vec<CppWork<'tree>>,
9362        ancestry: &ParentIndex<'tree>,
9363    ) -> bool {
9364        if self.visit_nested_namespace_sentinel(node, scope, ancestry) {
9365            return true;
9366        }
9367        if let Some((
9368            reparse_start,
9369            class_start,
9370            body_start,
9371            class_close_start,
9372            class_close_end,
9373            class_close_line,
9374        )) = cpp_sentinel_macro_class_region(node, self.source)
9375        {
9376            let Some(class_tree) =
9377                cpp_reparse_region_items(self.source, reparse_start, class_close_end)
9378            else {
9379                return false;
9380            };
9381            let class_root = class_tree.root_node();
9382            let template_node = cpp_sentinel_reparsed_leading_template(class_root);
9383            // A region reparse is its own tree and needs its own parent index.
9384            let class_ancestry = self.recovery_ancestry(class_root);
9385            let Some(reparsed_class) = cpp_sentinel_reparsed_class(
9386                class_root,
9387                template_node,
9388                self.source,
9389                &class_ancestry,
9390            ) else {
9391                return false;
9392            };
9393            let class_node = reparsed_class.declaration_node;
9394            let name = reparsed_class.name;
9395            let mut class_scope = scope.clone();
9396            if let Some(template_node) = template_node {
9397                class_scope.template_signature =
9398                    cpp_template_signature(template_node, class_node, self.source);
9399                class_scope.template_metadata =
9400                    cpp_template_metadata(template_node, class_node, self.source, &class_ancestry);
9401            }
9402            let Some(body_tree) =
9403                cpp_reparse_region_items(self.source, body_start, class_close_start)
9404            else {
9405                return false;
9406            };
9407            let raw_supertypes = reparsed_class.raw_supertypes;
9408            let base_facts = reparsed_class.base_facts;
9409            let class_range = Range {
9410                start_byte: class_start,
9411                end_byte: class_close_end,
9412                start_line: class_node.start_position().row + 1,
9413                end_line: class_close_line,
9414            };
9415            let class_scope = self.scope_for_recovered_exported_class(
9416                class_node,
9417                &name,
9418                true,
9419                &class_scope,
9420                &class_ancestry,
9421            );
9422            let mut class_stack = Vec::new();
9423            self.with_recovery_source_context(node, |visitor| {
9424                let class_unit = visitor.visit_named_class_like_shape(
9425                    class_node,
9426                    name,
9427                    None,
9428                    true,
9429                    Some(class_range),
9430                    raw_supertypes,
9431                    base_facts,
9432                    &class_scope,
9433                    &mut class_stack,
9434                    &class_ancestry,
9435                );
9436                visitor.parsed.record_materialization(
9437                    MaterializationRecord::RecoveredDeclaration {
9438                        recovery: class_range,
9439                        unit: class_unit.clone(),
9440                    },
9441                );
9442                let member_scope = ScopeInfo {
9443                    package_name: class_scope.package_name.clone(),
9444                    module: class_scope.module.clone(),
9445                    class_unit: Some(class_unit),
9446                    template_signature: class_scope.template_signature.clone(),
9447                    template_metadata: None,
9448                    declarations_are_fields: true,
9449                    recovered_specialization_member_scope: false,
9450                    visible_using_namespaces: class_scope.visible_using_namespaces.clone(),
9451                    local_block: None,
9452                    visible_using_declarations: class_scope.visible_using_declarations.clone(),
9453                };
9454                // The padded body reparse is its own tree, so it indexes itself.
9455                let body_root = body_tree.root_node();
9456                visitor.run_container_work(
9457                    body_root,
9458                    member_scope,
9459                    &visitor.recovery_ancestry(body_root),
9460                );
9461            });
9462            // Register only after the padded body reparse: its nodes deliberately
9463            // retain offsets inside the consumed region and must be visited first.
9464            self.consumed_fragment_regions
9465                .push((node.start_byte(), class_close_end));
9466            // An ERROR envelope can hold real sibling declarations after the
9467            // recovered class's close (the suffix-reparse boundary in
9468            // `cpp_sentinel_macro_class_region` partitions, it does not
9469            // consume). Walk the envelope's remaining children normally; the
9470            // consumed region above keeps the recovered class from being
9471            // indexed twice.
9472            if node.kind() == "ERROR" && node.end_byte() > class_close_end {
9473                stack.push(CppWork::Container(CppContainer {
9474                    node,
9475                    scope: scope.clone(),
9476                }));
9477            }
9478            return true;
9479        }
9480        let Some((start, end)) = cpp_sentinel_macro_region(node, self.source) else {
9481            return false;
9482        };
9483        let Some(tree) = cpp_reparse_region_items(self.source, start, end) else {
9484            return false;
9485        };
9486        let root = tree.root_node();
9487        if !cpp_reparsed_items_are_indexable(root, self.source) {
9488            return false;
9489        }
9490        let recovery = cpp_recovery_window(self.source, start, end);
9491        // The reparsed region is its own tree, so this walk indexes it itself.
9492        let reparsed_ancestry = self.recovery_ancestry(root);
9493        self.record_recovered_declarations(recovery, node, |visitor| {
9494            visitor.visit_container(
9495                root,
9496                &reparsed_ancestry,
9497                &scope.package_name,
9498                scope.module.clone(),
9499                scope.class_unit.clone(),
9500                scope.template_signature.clone(),
9501                scope.visible_using_namespaces.clone(),
9502                scope.visible_using_declarations.clone(),
9503            );
9504        });
9505        if end > node.end_byte() {
9506            self.consumed_fragment_regions
9507                .push((node.start_byte(), end));
9508        } else if node.kind() == "ERROR" && node.end_byte() > end {
9509            // The sentinel region ended at the first recovered class-like item
9510            // but the ERROR envelope keeps real sibling declarations after it
9511            // (fmt's color.h: `enum class color` under stacked FMT_BEGIN
9512            // sentinels, followed by `terminal_color`, `rgb`, ...). Walk the
9513            // envelope's remaining children normally; the consumed region
9514            // keeps the reparsed prefix from being indexed twice.
9515            self.consumed_fragment_regions
9516                .push((node.start_byte(), end));
9517            stack.push(CppWork::Container(CppContainer {
9518                node,
9519                scope: scope.clone(),
9520            }));
9521        }
9522        true
9523    }
9524
9525    /// Re-own complete class declarations from the structured Abseil
9526    /// namespace-sentinel shape.  The malformed root `ERROR` is not reparsed:
9527    /// its direct CST children already prove both namespace components and the
9528    /// class bodies, so the ordinary class/member visitor can retain ownership
9529    /// and exact source ranges without admitting unrelated callable bodies.
9530    fn visit_nested_namespace_sentinel<'tree>(
9531        &mut self,
9532        node: Node<'tree>,
9533        scope: &ScopeInfo,
9534        ancestry: &ParentIndex<'tree>,
9535    ) -> bool {
9536        let Some(recovered) = cpp_nested_namespace_sentinel(node, self.source, ancestry) else {
9537            return false;
9538        };
9539        let sentinel_namespace = cpp_sentinel_recovered_namespace_components(
9540            recovered.function,
9541            &recovered.namespace_components,
9542            self.source,
9543        );
9544
9545        let mut package_name = scope.package_name.clone();
9546        let mut module = scope.module.clone();
9547        for component in recovered.namespace_components {
9548            package_name = if package_name.is_empty() {
9549                component
9550            } else {
9551                format!("{package_name}::{component}")
9552            };
9553            let namespace_module = CodeUnit::new_fq(
9554                self.file.clone(),
9555                CodeUnitType::Module,
9556                "",
9557                package_name.clone(),
9558                cpp_namespace_fq(&package_name),
9559            );
9560            if !self.parsed.contains_declaration(&namespace_module) {
9561                self.add_declaration(
9562                    ancestry,
9563                    namespace_module.clone(),
9564                    recovered.function,
9565                    None,
9566                    None,
9567                );
9568            }
9569            module = Some(namespace_module);
9570        }
9571
9572        let recovered_scope = ScopeInfo {
9573            package_name,
9574            module,
9575            // This scope's package comes from the namespace levels the sentinel
9576            // shape proves, so its owner must come from the same place. C++
9577            // never nests a namespace inside a class, so a surviving
9578            // `class_unit` here is recovery bleed from an earlier malformed
9579            // region, and keeping it would publish declarations whose package
9580            // names one lexical position and whose owner names another -- the
9581            // hybrid identity that trips the package/short boundary assert in
9582            // `CodeUnit::with_signature_and_fq` (#2306, #2979). Dropping it is
9583            // identity-neutral for valid code, where `class_unit` is always
9584            // empty at a namespace.
9585            class_unit: None,
9586            template_signature: scope.template_signature.clone(),
9587            template_metadata: scope.template_metadata.clone(),
9588            declarations_are_fields: false,
9589            recovered_specialization_member_scope: false,
9590            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9591            local_block: None,
9592            visible_using_declarations: scope.visible_using_declarations.clone(),
9593        };
9594        if let Some(fragmented) = cpp_sentinel_fragmented_class_tail(
9595            recovered.function,
9596            recovered.body,
9597            self.source,
9598            ancestry,
9599        ) {
9600            let mut class_scope = recovered_scope.clone();
9601            if let Some(template_node) = fragmented.template_node {
9602                class_scope.template_signature =
9603                    cpp_template_signature(template_node, fragmented.class_node, self.source);
9604                class_scope.template_metadata = cpp_template_metadata(
9605                    template_node,
9606                    fragmented.class_node,
9607                    self.source,
9608                    ancestry,
9609                );
9610            }
9611            if let Some(outcome) = self
9612                .reparse_fragmented_export_class_members(&fragmented.fragmented, &fragmented.name)
9613            {
9614                let mut class_stack = Vec::new();
9615                let class_unit = self.visit_named_class_like_shape(
9616                    fragmented.class_node,
9617                    fragmented.name.clone(),
9618                    None,
9619                    true,
9620                    Some(fragmented.fragmented.class_range),
9621                    fragmented.raw_supertypes.clone(),
9622                    Some(extract_cpp_base_facts(fragmented.class_node, self.source)),
9623                    &class_scope,
9624                    &mut class_stack,
9625                    ancestry,
9626                );
9627                self.parsed
9628                    .record_materialization(MaterializationRecord::RecoveredDeclaration {
9629                        recovery: fragmented.fragmented.class_range,
9630                        unit: class_unit.clone(),
9631                    });
9632                let mut class_lexical_scope = sentinel_namespace.clone();
9633                class_lexical_scope.push(fragmented.name.clone());
9634                self.visit_sentinel_class_aliases(
9635                    cpp_sentinel_fragmented_class_aliases(&fragmented),
9636                    &class_unit,
9637                    &class_scope,
9638                    &class_lexical_scope,
9639                    ancestry,
9640                );
9641                if self.visit_fragmented_export_class_members(
9642                    node,
9643                    outcome,
9644                    class_unit,
9645                    &class_scope,
9646                ) {
9647                    self.consumed_fragment_regions.push((
9648                        fragmented.consumed_start,
9649                        fragmented.fragmented.class_range.end_byte,
9650                    ));
9651                }
9652            }
9653        }
9654        // The class requirement above is the admission gate; once admitted,
9655        // traverse the whole proven inner namespace body so sibling aliases,
9656        // functions, and variables are not silently discarded. The body is a
9657        // node of the tree being walked, so it reuses that tree's index.
9658        self.run_container_work(recovered.body, recovered_scope, ancestry);
9659        true
9660    }
9661
9662    /// Publish the original structured aliases of a recovered sentinel class
9663    /// as its members, then claim their byte ranges. The claim keeps the member
9664    /// reparse from publishing its statement-context misreading of the same
9665    /// text, and keeps the outer walk from publishing the alias again at
9666    /// namespace scope when the reparse leaves the class region unclaimed.
9667    ///
9668    /// The malformed CST ancestry names only the namespaces outside the
9669    /// sentinel, so the target type is read in `lexical_scope`, the recovered
9670    /// class scope the usage extractor also assigns to these nodes.
9671    fn visit_sentinel_class_aliases<'tree>(
9672        &mut self,
9673        aliases: Vec<Node<'tree>>,
9674        class_unit: &CodeUnit,
9675        scope: &ScopeInfo,
9676        lexical_scope: &[String],
9677        ancestry: &ParentIndex<'tree>,
9678    ) {
9679        let member_scope = ScopeInfo {
9680            package_name: class_unit.package_name().to_string(),
9681            module: scope.module.clone(),
9682            class_unit: Some(class_unit.clone()),
9683            template_signature: scope.template_signature.clone(),
9684            template_metadata: None,
9685            declarations_are_fields: true,
9686            recovered_specialization_member_scope: false,
9687            visible_using_namespaces: scope.visible_using_namespaces.clone(),
9688            local_block: None,
9689            visible_using_declarations: scope.visible_using_declarations.clone(),
9690        };
9691        for alias in aliases {
9692            let mut alias_scope = member_scope.clone();
9693            let declaration = if alias.kind() == "template_declaration" {
9694                let declaration = alias
9695                    .named_children(&mut alias.walk())
9696                    .find(|child| child.kind() == "alias_declaration")
9697                    .expect("an admitted template alias holds its alias declaration");
9698                alias_scope.template_signature =
9699                    cpp_template_signature(alias, declaration, self.source);
9700                alias_scope.template_metadata =
9701                    cpp_template_metadata(alias, declaration, self.source, ancestry);
9702                declaration
9703            } else {
9704                alias
9705            };
9706            self.consumed_fragment_regions
9707                .push((alias.start_byte(), alias.end_byte()));
9708            let Some(name) = extract_alias_declaration_name(declaration, self.source) else {
9709                continue;
9710            };
9711            let signature = normalize_cpp_whitespace(node_text(declaration, self.source));
9712            let unit = self.type_alias_unit(&alias_scope, name.clone(), signature.clone());
9713            self.record_type_aliases(
9714                ancestry,
9715                declaration,
9716                &alias_scope,
9717                vec![name.clone()],
9718                signature,
9719                cpp_declaration_range(declaration),
9720                lexical_scope,
9721            );
9722            // The source fact reads its lexical path from the same malformed
9723            // ancestry; structural alias navigation joins this path.
9724            if let Some(declaration) = self
9725                .source_links
9726                .get(&unit)
9727                .and_then(|declarations| declarations.last())
9728            {
9729                let mut properties = self.source_properties.borrow_mut();
9730                let fact = properties
9731                    .get_mut(declaration)
9732                    .expect("a linked alias has a source fact");
9733                fact.lexical_path = lexical_scope.to_vec();
9734                fact.lexical_path.push(name);
9735            }
9736        }
9737    }
9738
9739    fn visit_declaration<'tree>(
9740        &mut self,
9741        node: Node<'tree>,
9742        scope: &ScopeInfo,
9743        in_class_body: bool,
9744        stack: &mut Vec<CppWork<'tree>>,
9745        ancestry: &ParentIndex<'tree>,
9746    ) {
9747        if self.visit_sentinel_macro_region(node, scope, stack, ancestry) {
9748            return;
9749        }
9750        if in_class_body && self.visit_bare_object_macro_fields(node, scope) {
9751            return;
9752        }
9753        // Statement-context parsing treats Ctor(int value) as a parenthesized
9754        // variable declarator. Validate it as a class member before publishing
9755        // its parameter name as a field.
9756        if in_class_body
9757            && scope.class_unit.as_ref().is_some_and(|owner| {
9758                node.child_by_field_name("type").is_some_and(|ty| {
9759                    ty.kind() == "type_identifier"
9760                        && node_text(ty, self.source) == owner.identifier()
9761                })
9762            })
9763            && node
9764                .child_by_field_name("declarator")
9765                .is_some_and(|declarator| declarator.kind() == "parenthesized_declarator")
9766            && self.visit_bare_constructor_expression(node, scope, ancestry)
9767        {
9768            return;
9769        }
9770        if recovered_macro_return_type_node(node, self.source).is_some_and(|declarator| {
9771            !cpp_active_template_type_parameter(
9772                node,
9773                node_text(declarator, self.source),
9774                self.source,
9775                ancestry,
9776            )
9777        }) {
9778            return;
9779        }
9780        if in_class_body && let Some(recovered) = recovered_pyobject_head_field(node, self.source) {
9781            // `PyObject_HEAD` is an object-like macro, so tree-sitter folds
9782            // the following `Imaging image` member into one malformed field.
9783            // The ERROR's identifier is the actual declarator; route it
9784            // through the ordinary field path so its parent, range, and
9785            // signature metadata remain consistent with every other member.
9786            self.visit_variable_declaration(node, recovered.declarator, scope, true, ancestry);
9787            return;
9788        }
9789        if in_class_body
9790            && let Some(parent) = scope.class_unit.as_ref()
9791            && let Some(call) =
9792                recovered_macro_qualified_constructor_call(node, parent.identifier(), self.source)
9793        {
9794            self.visit_recovered_macro_qualified_constructor_definition(
9795                node, call, scope, ancestry,
9796            );
9797            return;
9798        }
9799        if in_class_body
9800            && let Some(parent) = scope.class_unit.as_ref()
9801            && let Some(constructor) = recovered_macro_initialized_constructor_declarator(
9802                node,
9803                parent.identifier(),
9804                self.source,
9805            )
9806        {
9807            self.visit_recovered_macro_initialized_constructor_definition(
9808                node,
9809                constructor,
9810                scope,
9811                ancestry,
9812            );
9813            return;
9814        }
9815        if in_class_body
9816            && let Some(call) = recovered_macro_qualified_function_call(node, self.source)
9817        {
9818            self.visit_recovered_macro_qualified_function_declaration(node, call, scope, ancestry);
9819            return;
9820        }
9821        if in_class_body
9822            && let Some(members) = string_attribute_macro_member_declarators(node, self.source)
9823        {
9824            for member in members {
9825                self.add_macro_wrapped_declaration(member, scope, ancestry);
9826            }
9827            return;
9828        }
9829        if in_class_body && self.visit_string_attribute_macro_type_members(node, scope) {
9830            return;
9831        }
9832        if in_class_body
9833            && let Some(declarators) =
9834                recovered_macro_qualified_field_declarators(node, self.source)
9835        {
9836            for declarator in declarators {
9837                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
9838            }
9839            return;
9840        }
9841        let recovered_alias_names = recovered_type_alias_names(node, self.source);
9842        if !recovered_alias_names.is_empty() {
9843            self.add_type_aliases(ancestry, node, scope, recovered_alias_names);
9844            return;
9845        }
9846        if self.visit_anonymous_aggregate_declaration(node, scope, in_class_body, stack, ancestry) {
9847            return;
9848        }
9849        if self.visit_anonymous_local_aggregate_declaration(node, scope, stack, ancestry) {
9850            return;
9851        }
9852
9853        if let Some(recovered) = recover_exported_class_declaration(node, self.source) {
9854            if let Some(fragmented) = recovered.fragmented_body.as_ref() {
9855                // Issue #938: the members tree-sitter scattered out of the fragmented
9856                // multiple-base export node are reparsed from their true body region
9857                // and re-owned as members of the recovered class, with an explicit
9858                // navigation range spanning to the displaced closing brace.
9859                if let Some(outcome) =
9860                    self.reparse_fragmented_export_class_members(fragmented, &recovered.name)
9861                {
9862                    let consumed_region = (
9863                        recovered.declaration_node.end_byte(),
9864                        fragmented.class_range.end_byte,
9865                    );
9866                    let code_unit = self.visit_named_class_like_shape(
9867                        recovered.declaration_node,
9868                        recovered.name,
9869                        None,
9870                        true,
9871                        Some(fragmented.class_range),
9872                        recovered.raw_supertypes,
9873                        recovered.base_facts,
9874                        scope,
9875                        stack,
9876                        ancestry,
9877                    );
9878                    self.parsed.record_materialization(
9879                        MaterializationRecord::RecoveredDeclaration {
9880                            recovery: fragmented.class_range,
9881                            unit: code_unit.clone(),
9882                        },
9883                    );
9884                    let consume_fragment =
9885                        self.visit_fragmented_export_class_members(node, outcome, code_unit, scope);
9886                    // Everything between the fragmented declaration and its displaced
9887                    // closing brace now belongs to the recovered class; keep the
9888                    // ordinary walk from re-indexing those scattered siblings at top
9889                    // level. Register the consumed region only after indexing because
9890                    // the reparsed nodes retain byte offsets inside that same region.
9891                    if consume_fragment {
9892                        self.consumed_fragment_regions.push(consumed_region);
9893                    }
9894                    return;
9895                }
9896            }
9897            let uses_initializer_body = recovered.uses_initializer_body;
9898            let definition_body_present = recovered.body.is_some();
9899            let class_unit = self.visit_named_class_like_shape(
9900                recovered.declaration_node,
9901                recovered.name,
9902                recovered.body,
9903                definition_body_present,
9904                None,
9905                recovered.raw_supertypes,
9906                recovered.base_facts,
9907                scope,
9908                stack,
9909                ancestry,
9910            );
9911            self.parsed
9912                .record_materialization(MaterializationRecord::RecoveredDeclaration {
9913                    recovery: cpp_declaration_range(node),
9914                    unit: class_unit,
9915                });
9916            if uses_initializer_body {
9917                return;
9918            }
9919        }
9920
9921        // A function-like macro invocation that expands to declarations keeps
9922        // no terminator of its own: the semicolons live in the replacement
9923        // body, which is not part of this tree. Tree-sitter recovers one and
9924        // reads the macro name as the member type and its argument list as a
9925        // parenthesized declarator, so `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)`
9926        // published a field named `ClosedDetect` beside the real constructor
9927        // and split every reference to the constructor's name between the two
9928        // (#3298). The invented terminator is the whole difference from a real
9929        // member: a member terminates itself, and with a terminator the
9930        // grammar reads `T (name);` as a declaration rather than a field.
9931        if in_class_body && cpp_unterminated_macro_invocation_field(node) {
9932            return;
9933        }
9934
9935        let mut handled_function = false;
9936        let mut handled_declarator = false;
9937        let mut cursor = node.walk();
9938        for child in node.named_children(&mut cursor) {
9939            if matches!(
9940                child.kind(),
9941                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
9942            ) {
9943                // A named class-like definition remains a declaration even when
9944                // the same statement also declares an object, for example
9945                // `enum Kind { A } kind;`.  Tree-sitter exposes the enum as the
9946                // declaration's type and `kind` as its declarator.  Dropping the
9947                // type here loses both its nested owner and every later lexical
9948                // reference to it.  A body is the structured proof that this is
9949                // a definition rather than an elaborated type use such as
9950                // `class Kind value;`.
9951                if cpp_body_node(child).is_some() {
9952                    self.visit_class_like(child, scope, stack, ancestry);
9953                }
9954                continue;
9955            }
9956        }
9957
9958        let mut cursor = node.walk();
9959        for child in node.children_by_field_name("declarator", &mut cursor) {
9960            if crate::structural::is_recovered_designator_init_declarator(child) {
9961                handled_declarator = true;
9962                continue;
9963            }
9964            if in_class_body
9965                && let Some(field) = recovered_function_like_field_declarator(node, self.source)
9966            {
9967                handled_declarator = true;
9968                self.visit_variable_declaration(node, field.name, scope, true, ancestry);
9969                continue;
9970            }
9971            if let Some(kind) = classify_declarator(child) {
9972                handled_declarator = true;
9973                match kind {
9974                    DeclaratorKind::Function(function_declarator) => {
9975                        handled_function = true;
9976                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
9977                    }
9978                    DeclaratorKind::Variable(variable_declarator) => {
9979                        self.visit_variable_declaration(
9980                            node,
9981                            variable_declarator,
9982                            scope,
9983                            in_class_body,
9984                            ancestry,
9985                        );
9986                    }
9987                }
9988            }
9989        }
9990
9991        if !handled_declarator {
9992            let mut cursor = node.walk();
9993            for child in node.named_children(&mut cursor) {
9994                if crate::structural::is_recovered_designator_init_declarator(child) {
9995                    handled_declarator = true;
9996                    continue;
9997                }
9998                if !is_unfielded_declarator_candidate(child) {
9999                    continue;
10000                }
10001                let Some(kind) = classify_declarator(child) else {
10002                    continue;
10003                };
10004                handled_declarator = true;
10005                match kind {
10006                    DeclaratorKind::Function(function_declarator) => {
10007                        handled_function = true;
10008                        self.visit_function_declaration(node, function_declarator, scope, ancestry);
10009                    }
10010                    DeclaratorKind::Variable(variable_declarator) => {
10011                        self.visit_variable_declaration(
10012                            node,
10013                            variable_declarator,
10014                            scope,
10015                            in_class_body,
10016                            ancestry,
10017                        );
10018                    }
10019                }
10020            }
10021        }
10022
10023        if handled_function {
10024            return;
10025        }
10026
10027        if !handled_declarator {
10028            if in_class_body {
10029                self.visit_class_members_from_declaration(node, scope, ancestry);
10030            } else {
10031                self.visit_global_variables_from_declaration(node, scope, ancestry);
10032            }
10033        }
10034    }
10035
10036    /// Preserve the member structure of an anonymous aggregate.
10037    ///
10038    /// An anonymous union with no declarator promotes its fields into the
10039    /// containing aggregate. An anonymous struct/union followed by a named
10040    /// declarator, such as `struct { T *ops; } sock`, declares both the field
10041    /// `sock` and an otherwise unnamed receiver type. Give that receiver type
10042    /// the declarator's structured nested identity so a later `value.sock.ops`
10043    /// chain can traverse it without parsing a type spelling (#2407).
10044    ///
10045    /// An anonymous aggregate declares no tag, so there is nothing for the C
10046    /// dialect to re-scope and nothing for the C++ dialect to nest: both
10047    /// readings of this shape are the same, and both need these members
10048    /// (#3301).
10049    fn anonymous_aggregate_declaration_parts<'tree>(
10050        node: Node<'tree>,
10051    ) -> Option<(Node<'tree>, Node<'tree>, Vec<Node<'tree>>)> {
10052        let aggregate = node.child_by_field_name("type")?;
10053        if !matches!(aggregate.kind(), "struct_specifier" | "union_specifier")
10054            || aggregate.child_by_field_name("name").is_some()
10055        {
10056            return None;
10057        }
10058        let body = cpp_body_node(aggregate)?;
10059        let mut cursor = node.walk();
10060        let declarators = node
10061            .children_by_field_name("declarator", &mut cursor)
10062            .filter_map(|declarator| match classify_declarator(declarator) {
10063                Some(DeclaratorKind::Variable(variable)) => Some(variable),
10064                Some(DeclaratorKind::Function(_)) | None => None,
10065            })
10066            .collect();
10067        Some((aggregate, body, declarators))
10068    }
10069
10070    fn visit_anonymous_aggregate_declaration<'tree>(
10071        &mut self,
10072        node: Node<'tree>,
10073        scope: &ScopeInfo,
10074        in_class_body: bool,
10075        stack: &mut Vec<CppWork<'tree>>,
10076        ancestry: &ParentIndex<'tree>,
10077    ) -> bool {
10078        if !in_class_body || scope.class_unit.is_none() {
10079            return false;
10080        }
10081        let Some((aggregate, body, declarators)) =
10082            Self::anonymous_aggregate_declaration_parts(node)
10083        else {
10084            return false;
10085        };
10086        if declarators.is_empty() {
10087            stack.push(CppWork::Container(CppContainer {
10088                node: body,
10089                scope: scope.clone(),
10090            }));
10091            return true;
10092        }
10093
10094        for declarator in declarators {
10095            let Some(name) = extract_variable_name(declarator, self.source) else {
10096                continue;
10097            };
10098            self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10099            // The receiver type's identity is the declarator's name, so its
10100            // range is the field declaration that carries that name token.
10101            // The inner specifier ends at the closing brace, which leaves the
10102            // name the unit is known by outside its own text (#3287).
10103            self.visit_named_class_like_shape(
10104                aggregate,
10105                name,
10106                Some(body),
10107                true,
10108                Some(cpp_declaration_range(node)),
10109                None,
10110                None,
10111                scope,
10112                stack,
10113                ancestry,
10114            );
10115        }
10116        true
10117    }
10118
10119    /// Give a function-local anonymous aggregate a structured owner so its
10120    /// direct pointer bindings can be typed by the usage graph.  Unlike an
10121    /// anonymous aggregate in a class body, there is no source-level tag or
10122    /// typedef to supply an identity. Its rank within the named callable owner
10123    /// supplies a generated identity; no source spelling is consulted.  The declaration's own variable remains a file-level
10124    /// field projection, while the aggregate body is visited as the generated
10125    /// class's field list.
10126    fn visit_anonymous_local_aggregate_declaration<'tree>(
10127        &mut self,
10128        node: Node<'tree>,
10129        scope: &ScopeInfo,
10130        stack: &mut Vec<CppWork<'tree>>,
10131        ancestry: &ParentIndex<'tree>,
10132    ) -> bool {
10133        if scope.class_unit.is_some() || !has_function_scope_ancestor(node) {
10134            return false;
10135        }
10136        let Some((aggregate, body, declarators)) =
10137            Self::anonymous_aggregate_declaration_parts(node)
10138        else {
10139            return false;
10140        };
10141        if declarators.is_empty() {
10142            return false;
10143        }
10144
10145        for declarator in &declarators {
10146            self.visit_variable_declaration(node, *declarator, scope, false, ancestry);
10147        }
10148        let synthetic = cpp_synthetic_name(aggregate);
10149        let name = format!("{}@{}", synthetic.kind, synthetic.rank);
10150        self.visit_named_class_like_shape(
10151            aggregate,
10152            name,
10153            Some(body),
10154            true,
10155            None,
10156            None,
10157            None,
10158            scope,
10159            stack,
10160            ancestry,
10161        );
10162        true
10163    }
10164
10165    fn visit_function_declaration<'tree>(
10166        &mut self,
10167        declaration_node: Node<'tree>,
10168        declarator: Node<'tree>,
10169        scope: &ScopeInfo,
10170        ancestry: &ParentIndex<'tree>,
10171    ) {
10172        let Some(function) = extract_function_info(declarator, self.source, scope) else {
10173            return;
10174        };
10175        // An in-class member prototype is a real source declaration, not a
10176        // generated or recovery-only unit, so it keeps the ordinary code-unit
10177        // identity (#3508). #3493 recorded its callable modifiers, which is
10178        // what a canonical procedure key needs; a synthetic unit would still
10179        // refuse that key on identity grounds alone.
10180        let code_unit = function.code_unit(self.file.clone());
10181        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10182            self.callable_observer.observe_duplicate(
10183                &code_unit,
10184                declaration_node.start_byte(),
10185                declarator,
10186                self.source,
10187                ancestry,
10188                &|| {
10189                    let signature = render_cpp_function_display_signature_from_node(
10190                        declaration_node,
10191                        self.source,
10192                        scope.template_signature.as_deref(),
10193                        false,
10194                        ancestry,
10195                    );
10196                    cpp_signature_metadata(
10197                        signature,
10198                        declarator,
10199                        cpp_callable_linkage(declaration_node, self.source, ancestry),
10200                        self.source,
10201                        ancestry,
10202                    )
10203                    .with_declaration_only(true)
10204                },
10205            );
10206            return;
10207        }
10208        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10209        let signature = render_cpp_function_display_signature_from_node(
10210            declaration_node,
10211            self.source,
10212            scope.template_signature.as_deref(),
10213            false,
10214            ancestry,
10215        );
10216        self.add_callable_signature_with_metadata(
10217            code_unit.clone(),
10218            declaration_node.start_byte(),
10219            declarator,
10220            ancestry,
10221            cpp_signature_metadata(
10222                signature,
10223                declarator,
10224                cpp_callable_linkage(declaration_node, self.source, ancestry),
10225                self.source,
10226                ancestry,
10227            )
10228            .with_declaration_only(true),
10229        );
10230        if let Some(parent) = &scope.class_unit {
10231            self.parsed.add_child(parent.clone(), code_unit);
10232        } else if let Some(module) = &scope.module {
10233            self.parsed.add_child(module.clone(), code_unit);
10234        }
10235    }
10236
10237    fn link_existing_source_declaration(
10238        &mut self,
10239        ancestry: &ParentIndex<'_>,
10240        code_unit: &CodeUnit,
10241        declaration_node: Node<'_>,
10242    ) -> bool {
10243        if !self.parsed.contains_declaration(code_unit) {
10244            return false;
10245        }
10246        self.link_source_declaration(ancestry, code_unit, declaration_node);
10247        self.parsed
10248            .record_navigation_range(code_unit.clone(), cpp_declaration_range(declaration_node));
10249        true
10250    }
10251
10252    fn visit_recovered_macro_qualified_function_declaration<'tree>(
10253        &mut self,
10254        declaration_node: Node<'tree>,
10255        call: Node<'tree>,
10256        scope: &ScopeInfo,
10257        ancestry: &ParentIndex<'tree>,
10258    ) {
10259        let Some(parent) = &scope.class_unit else {
10260            return;
10261        };
10262        let Some(name_node) = call.child_by_field_name("function") else {
10263            return;
10264        };
10265        let Some(arguments) = call.child_by_field_name("arguments") else {
10266            return;
10267        };
10268        let Some((signature, parameter_labels, discriminator)) =
10269            recovered_macro_qualified_function_parameters(arguments, self.source)
10270        else {
10271            return;
10272        };
10273        let arity = parameter_labels.len();
10274        let function = FunctionInfo {
10275            package_name: scope.package_name.clone(),
10276            owner: Some(CppMemberOwner::Unit(parent.clone())),
10277            name: normalize_cpp_whitespace(node_text(name_node, self.source)),
10278            signature,
10279        };
10280        if function.name.is_empty() {
10281            return;
10282        }
10283        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10284        if self.link_existing_source_declaration(ancestry, &code_unit, declaration_node) {
10285            return;
10286        }
10287        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10288        let signature_label = render_cpp_function_display_signature_from_node(
10289            declaration_node,
10290            self.source,
10291            scope.template_signature.as_deref(),
10292            false,
10293            ancestry,
10294        );
10295        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10296        // Only the callable's parameters came from the reparse: the
10297        // `field_declaration` this recovery reads is a real node in a real
10298        // class body, so its storage class and the body's access ladder state
10299        // this member's modifiers exactly as an ordinary member's do.
10300        let modifiers =
10301            cpp_callable_modifiers(declaration_node, None, false, self.source, ancestry);
10302        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10303            .with_declaration_only(true)
10304            .with_callable_arity(CallableArity::exact(arity))
10305            .with_overload_discriminator(Some(discriminator))
10306            .with_callable_linkage(linkage)
10307            .with_callable_modifiers(
10308                modifiers.is_static,
10309                modifiers.is_constructor,
10310                modifiers.visibility,
10311            );
10312        self.add_signature_with_metadata(code_unit.clone(), metadata);
10313        self.parsed.add_child(parent.clone(), code_unit);
10314    }
10315
10316    fn visit_recovered_macro_qualified_constructor_definition<'tree>(
10317        &mut self,
10318        declaration_node: Node<'tree>,
10319        call: Node<'tree>,
10320        scope: &ScopeInfo,
10321        ancestry: &ParentIndex<'tree>,
10322    ) {
10323        let Some(parent) = &scope.class_unit else {
10324            return;
10325        };
10326        let Some(arguments) = call.child_by_field_name("arguments") else {
10327            return;
10328        };
10329        let Some((mut signature, parameter_labels, discriminator)) =
10330            recovered_macro_qualified_function_parameters(arguments, self.source)
10331        else {
10332            return;
10333        };
10334        if let Some(template_signature) = &scope.template_signature {
10335            signature = format!("{template_signature}{signature}");
10336        }
10337        let arity = parameter_labels.len();
10338        let function = FunctionInfo {
10339            package_name: scope.package_name.clone(),
10340            owner: Some(CppMemberOwner::Unit(parent.clone())),
10341            name: parent.identifier().to_string(),
10342            signature,
10343        };
10344        let code_unit = function.code_unit_with_synthetic(self.file.clone(), true);
10345        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10346        let signature_label = normalize_cpp_whitespace(node_text(declaration_node, self.source));
10347        let linkage = cpp_callable_linkage(declaration_node, self.source, ancestry);
10348        // The recovery already proved this member is the class's constructor:
10349        // the reparsed call names the enclosing class. Everything else is read
10350        // from the real `field_declaration` the recovery found it in.
10351        let modifiers = cpp_callable_modifiers(declaration_node, None, true, self.source, ancestry);
10352        let metadata = SignatureMetadata::with_parameter_labels(signature_label, parameter_labels)
10353            .with_declaration_only(false)
10354            .with_callable_arity(CallableArity::exact(arity))
10355            .with_overload_discriminator(Some(discriminator))
10356            .with_callable_linkage(linkage)
10357            .with_callable_modifiers(
10358                modifiers.is_static,
10359                modifiers.is_constructor,
10360                modifiers.visibility,
10361            );
10362        self.add_signature_with_metadata(code_unit.clone(), metadata);
10363        self.parsed.add_child(parent.clone(), code_unit);
10364    }
10365
10366    /// Publish the constructor of the macro-decorated field shape that
10367    /// `recovered_macro_initialized_constructor_declarator` proves. The head
10368    /// is a real, clean function declarator, so it keeps the ordinary
10369    /// identity and metadata; the `default_value` initializer list is the
10370    /// constructor body, so the occurrence is a definition.
10371    fn visit_recovered_macro_initialized_constructor_definition<'tree>(
10372        &mut self,
10373        declaration_node: Node<'tree>,
10374        function_declarator: Node<'tree>,
10375        scope: &ScopeInfo,
10376        ancestry: &ParentIndex<'tree>,
10377    ) {
10378        let Some(parent) = &scope.class_unit else {
10379            return;
10380        };
10381        let Some(function) = extract_function_info(function_declarator, self.source, scope) else {
10382            return;
10383        };
10384        let code_unit = function.code_unit(self.file.clone());
10385        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10386        // Render the head the way an ordinary constructor definition renders
10387        // the text ahead of its body, member initializers included.
10388        let body_start = declaration_node
10389            .child_by_field_name("default_value")
10390            .map_or(declaration_node.end_byte(), |body| body.start_byte());
10391        let head =
10392            collapse_cpp_whitespace(self.source[declaration_node.start_byte()..body_start].trim());
10393        let signature = match scope.template_signature.as_deref() {
10394            Some(template_signature) => format!("template {template_signature} {head} {{...}}"),
10395            None => format!("{head} {{...}}"),
10396        };
10397        self.add_callable_signature_with_metadata(
10398            code_unit.clone(),
10399            declaration_node.start_byte(),
10400            function_declarator,
10401            ancestry,
10402            cpp_signature_metadata(
10403                signature,
10404                function_declarator,
10405                cpp_callable_linkage(declaration_node, self.source, ancestry),
10406                self.source,
10407                ancestry,
10408            )
10409            .with_declaration_only(false),
10410        );
10411        self.parsed.add_child(parent.clone(), code_unit);
10412    }
10413
10414    fn visit_variable_declaration<'tree>(
10415        &mut self,
10416        declaration_node: Node<'tree>,
10417        declarator: Node<'tree>,
10418        scope: &ScopeInfo,
10419        in_class_body: bool,
10420        ancestry: &ParentIndex<'tree>,
10421    ) {
10422        let Some(name) = extract_variable_name(declarator, self.source) else {
10423            return;
10424        };
10425        let parent = if in_class_body {
10426            let Some(parent) = &scope.class_unit else {
10427                return;
10428            };
10429            Some(parent)
10430        } else {
10431            None
10432        };
10433        let short_name = match parent {
10434            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10435            None => name.clone(),
10436        };
10437        let fq = cpp_leaf_fq(
10438            &scope.package_name,
10439            parent,
10440            &name,
10441            SegmentKind::Member,
10442            SegmentKind::Member,
10443        );
10444        let code_unit = CodeUnit::new_fq(
10445            self.file.clone(),
10446            CodeUnitType::Field,
10447            scope.package_name.clone(),
10448            short_name,
10449            fq,
10450        );
10451        if self.parsed.contains_declaration(&code_unit) {
10452            return;
10453        }
10454        self.add_declaration(ancestry, code_unit.clone(), declaration_node, None, None);
10455        self.add_signature_with_metadata(
10456            code_unit.clone(),
10457            SignatureMetadata::new(
10458                render_cpp_field_signature(declaration_node, declarator, self.source),
10459                Vec::new(),
10460            )
10461            .with_cpp_field_linkage(cpp_field_declaration_linkage(
10462                declaration_node,
10463                self.source,
10464                ancestry,
10465            )),
10466        );
10467        if let Some(parent) = &scope.class_unit {
10468            self.parsed.add_child(parent.clone(), code_unit);
10469        } else if let Some(module) = &scope.module {
10470            self.parsed.add_child(module.clone(), code_unit);
10471        }
10472    }
10473
10474    /// Index the field half of an omitted-semicolon pair the macro-declaration
10475    /// grammar folded into the following function definition's head. The
10476    /// function itself continues through the ordinary visitor path.
10477    fn visit_omitted_semicolon_macro_field<'tree>(
10478        &mut self,
10479        node: Node<'tree>,
10480        type_modifier: Node<'tree>,
10481        name_modifier: Node<'tree>,
10482        scope: &ScopeInfo,
10483        ancestry: &ParentIndex<'tree>,
10484    ) {
10485        let Some(name_node) = macro_modifier_identifier(name_modifier) else {
10486            return;
10487        };
10488        let name = normalize_cpp_whitespace(node_text(name_node, self.source));
10489        if name.is_empty() {
10490            return;
10491        }
10492        let range = Range {
10493            start_byte: type_modifier.start_byte(),
10494            end_byte: name_modifier.end_byte(),
10495            start_line: type_modifier.start_position().row + 1,
10496            end_line: name_modifier.end_position().row + 1,
10497        };
10498        self.record_macro_head_field(node, range, name, scope, ancestry);
10499    }
10500
10501    /// Index the macro-return stand-in field: a clean function definition whose
10502    /// macro-modified head returns an active template type parameter. The field
10503    /// is named after the return type and spans the head up to it, exactly the
10504    /// phantom `field_declaration` the older grammar emitted (#1129).
10505    fn visit_macro_return_template_parameter_field<'tree>(
10506        &mut self,
10507        node: Node<'tree>,
10508        head_start: Node<'tree>,
10509        type_node: Node<'tree>,
10510        scope: &ScopeInfo,
10511        ancestry: &ParentIndex<'tree>,
10512    ) {
10513        let name = normalize_cpp_whitespace(node_text(type_node, self.source));
10514        let range = Range {
10515            start_byte: head_start.start_byte(),
10516            end_byte: type_node.end_byte(),
10517            start_line: head_start.start_position().row + 1,
10518            end_line: type_node.end_position().row + 1,
10519        };
10520        self.record_macro_head_field(node, range, name, scope, ancestry);
10521    }
10522
10523    /// Record one field recovered from a function definition's macro head. The
10524    /// signature is the head's own text: like the older grammar's phantom
10525    /// field_declaration, it carries no semicolon because none was written.
10526    fn record_macro_head_field<'tree>(
10527        &mut self,
10528        node: Node<'tree>,
10529        range: Range,
10530        name: String,
10531        scope: &ScopeInfo,
10532        ancestry: &ParentIndex<'tree>,
10533    ) {
10534        if name.is_empty() {
10535            return;
10536        }
10537        let parent = scope.class_unit.as_ref();
10538        let short_name = match parent {
10539            Some(parent) => cpp_join_member_short(parent.short_name(), &name),
10540            None => name.clone(),
10541        };
10542        let code_unit = CodeUnit::new_fq(
10543            self.file.clone(),
10544            CodeUnitType::Field,
10545            scope.package_name.clone(),
10546            short_name,
10547            cpp_leaf_fq(
10548                &scope.package_name,
10549                parent,
10550                &name,
10551                SegmentKind::Member,
10552                SegmentKind::Member,
10553            ),
10554        );
10555        if self.parsed.contains_declaration(&code_unit) {
10556            return;
10557        }
10558        self.add_declaration_with_range(
10559            ancestry,
10560            code_unit.clone(),
10561            range,
10562            Some(node),
10563            parent.cloned(),
10564            None,
10565        );
10566        let signature = normalize_cpp_whitespace(&self.source[range.start_byte..range.end_byte]);
10567        self.add_signature_with_metadata(
10568            code_unit.clone(),
10569            SignatureMetadata::new(signature, Vec::new())
10570                .with_cpp_field_linkage(cpp_field_declaration_linkage(node, self.source, ancestry)),
10571        );
10572        if let Some(parent) = &scope.class_unit {
10573            self.parsed.add_child(parent.clone(), code_unit);
10574        } else if let Some(module) = &scope.module {
10575            self.parsed.add_child(module.clone(), code_unit);
10576        }
10577    }
10578
10579    fn variable_declarator_from_declaration_child<'tree>(
10580        child: Node<'tree>,
10581    ) -> Option<Node<'tree>> {
10582        if child.kind() == "init_declarator" {
10583            child.child_by_field_name("declarator")
10584        } else if matches!(
10585            child.kind(),
10586            "identifier"
10587                | "field_identifier"
10588                | "pointer_declarator"
10589                | "reference_declarator"
10590                | "array_declarator"
10591                | "parenthesized_declarator"
10592                | "macro_modifier_declarator"
10593        ) {
10594            Some(child)
10595        } else {
10596            None
10597        }
10598    }
10599
10600    fn visit_class_members_from_declaration<'tree>(
10601        &mut self,
10602        node: Node<'tree>,
10603        scope: &ScopeInfo,
10604        ancestry: &ParentIndex<'tree>,
10605    ) {
10606        let mut cursor = node.walk();
10607        for child in node.named_children(&mut cursor) {
10608            if let Some(declarator) = recovered_function_like_field_declarator(child, self.source) {
10609                self.visit_variable_declaration(node, declarator.name, scope, true, ancestry);
10610            } else if let Some(declarator) = Self::variable_declarator_from_declaration_child(child)
10611            {
10612                self.visit_variable_declaration(node, declarator, scope, true, ancestry);
10613            }
10614        }
10615    }
10616
10617    fn visit_global_variables_from_declaration<'tree>(
10618        &mut self,
10619        node: Node<'tree>,
10620        scope: &ScopeInfo,
10621        ancestry: &ParentIndex<'tree>,
10622    ) {
10623        let mut cursor = node.walk();
10624        for child in node.named_children(&mut cursor) {
10625            if let Some(declarator) = Self::variable_declarator_from_declaration_child(child) {
10626                self.visit_variable_declaration(node, declarator, scope, false, ancestry);
10627            }
10628        }
10629    }
10630
10631    fn visit_type_declaration<'tree>(
10632        &mut self,
10633        node: Node<'tree>,
10634        scope: &ScopeInfo,
10635        stack: &mut Vec<CppWork<'tree>>,
10636        ancestry: &ParentIndex<'tree>,
10637    ) {
10638        let type_node = node.child_by_field_name("type");
10639        if let Some(type_node) = type_node
10640            && matches!(
10641                type_node.kind(),
10642                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
10643            )
10644        {
10645            self.visit_class_like(type_node, scope, stack, ancestry);
10646        }
10647
10648        if let Some(recovered) = recovered_macro_typedef_alias(node, self.source) {
10649            let range = Range {
10650                start_byte: node.start_byte(),
10651                end_byte: recovered.end_node.end_byte(),
10652                start_line: node.start_position().row + 1,
10653                end_line: recovered.end_node.end_position().row + 1,
10654            };
10655            let signature = self
10656                .source
10657                .get(range.start_byte..range.end_byte)
10658                .map(normalize_cpp_whitespace)
10659                .unwrap_or_default();
10660            self.record_type_aliases(
10661                ancestry,
10662                node,
10663                scope,
10664                vec![recovered.name],
10665                signature,
10666                range,
10667                &cpp_callable_lexical_scope(node, self.source, ancestry),
10668            );
10669            return;
10670        }
10671
10672        let alias_names = match node.kind() {
10673            "alias_declaration" => extract_alias_declaration_name(node, self.source)
10674                .into_iter()
10675                .collect::<Vec<_>>(),
10676            "type_definition" => extract_typedef_alias_names(node, self.source),
10677            _ => Vec::new(),
10678        };
10679        let anonymous_aggregate = if let (Some(type_node), [alias_name]) =
10680            (type_node, alias_names.as_slice())
10681            && matches!(type_node.kind(), "struct_specifier" | "union_specifier")
10682            && type_node.child_by_field_name("name").is_none()
10683        {
10684            cpp_body_node(type_node).map(|body| (body, alias_name.clone()))
10685        } else {
10686            None
10687        };
10688        self.add_type_aliases(ancestry, node, scope, alias_names);
10689        if let Some((body, alias_name)) = anonymous_aggregate {
10690            // The typedef alias is also the only user-visible identity of an
10691            // anonymous aggregate. Reuse it as the member owner instead of
10692            // minting a second signatureless class with the same FQN. The
10693            // latter makes forward lookup ambiguous when conditional aliases
10694            // coexist and returns duplicate definitions even without guards.
10695            let signature = normalize_cpp_whitespace(node_text(node, self.source));
10696            let alias_unit = self.type_alias_unit(scope, alias_name, signature);
10697            debug_assert!(self.parsed.contains_declaration(&alias_unit));
10698            let mut nested_scope = scope.clone();
10699            nested_scope.class_unit = Some(alias_unit);
10700            nested_scope.template_signature = scope.template_signature.clone();
10701            nested_scope.template_metadata = None;
10702            nested_scope.declarations_are_fields = false;
10703            nested_scope.recovered_specialization_member_scope = false;
10704            stack.push(CppWork::Container(CppContainer {
10705                node: body,
10706                scope: nested_scope,
10707            }));
10708        }
10709    }
10710
10711    fn add_type_aliases(
10712        &mut self,
10713        ancestry: &ParentIndex<'_>,
10714        node: Node<'_>,
10715        scope: &ScopeInfo,
10716        alias_names: Vec<String>,
10717    ) {
10718        let signature = normalize_cpp_whitespace(node_text(node, self.source));
10719        self.record_type_aliases(
10720            ancestry,
10721            node,
10722            scope,
10723            alias_names,
10724            signature,
10725            cpp_declaration_range(node),
10726            &cpp_callable_lexical_scope(node, self.source, ancestry),
10727        );
10728    }
10729
10730    /// `lexical_scope` is where the alias target's names are looked up: the
10731    /// alias's CST ancestry, or the owner a structural recovery proved when
10732    /// the CST ancestry is malformed.
10733    #[allow(clippy::too_many_arguments)]
10734    fn record_type_aliases(
10735        &mut self,
10736        ancestry: &ParentIndex<'_>,
10737        node: Node<'_>,
10738        scope: &ScopeInfo,
10739        alias_names: Vec<String>,
10740        signature: String,
10741        range: Range,
10742        lexical_scope: &[String],
10743    ) {
10744        if signature.is_empty() {
10745            return;
10746        }
10747        let type_name = node
10748            .child_by_field_name("type")
10749            .and_then(|type_node| type_node.child_by_field_name("name"))
10750            .map(|name_node| normalize_cpp_whitespace(node_text(name_node, self.source)));
10751        for alias_name in alias_names {
10752            if alias_name.is_empty() || type_name.as_deref() == Some(alias_name.as_str()) {
10753                continue;
10754            }
10755            let code_unit = self.type_alias_unit(scope, alias_name, signature.clone());
10756            // Declaration identity does not include the alias signature. Keep
10757            // each physical range so conditional aliases retain their guards.
10758            self.note_declaration(&code_unit);
10759            self.link_source_declaration(ancestry, &code_unit, node);
10760            self.parsed
10761                .add_code_unit_with_range(code_unit.clone(), range, None, None);
10762            let underlying_type_identity = node.child_by_field_name("type").and_then(|type_node| {
10763                cpp_structured_type_identity(type_node, self.source, lexical_scope)
10764            });
10765            self.add_signature_with_metadata(
10766                code_unit.clone(),
10767                SignatureMetadata::new(signature.clone(), Vec::new())
10768                    .with_underlying_type_identity(underlying_type_identity),
10769            );
10770            if let Some(metadata) = &scope.template_metadata {
10771                let mut metadata = metadata.clone();
10772                metadata.primary_fq_name = code_unit.fq_name();
10773                self.parsed
10774                    .set_cpp_template_metadata(code_unit.clone(), metadata);
10775            }
10776            if let Some(parent) = &scope.class_unit {
10777                self.parsed.add_child(parent.clone(), code_unit.clone());
10778            } else if let Some(module) = &scope.module {
10779                self.parsed.add_child(module.clone(), code_unit.clone());
10780            }
10781            self.parsed.mark_type_alias(code_unit);
10782        }
10783    }
10784
10785    fn type_alias_unit(
10786        &self,
10787        scope: &ScopeInfo,
10788        alias_name: String,
10789        signature: String,
10790    ) -> CodeUnit {
10791        let short_name = if let Some(parent) = &scope.class_unit {
10792            cpp_join_nested_short(parent.short_name(), &alias_name)
10793        } else {
10794            alias_name.clone()
10795        };
10796        let fq = cpp_leaf_fq(
10797            &scope.package_name,
10798            scope.class_unit.as_ref(),
10799            &alias_name,
10800            SegmentKind::Nested,
10801            SegmentKind::Type,
10802        );
10803        CodeUnit::with_signature_and_fq(
10804            self.file.clone(),
10805            CodeUnitType::Class,
10806            scope.package_name.clone(),
10807            short_name,
10808            Some(signature),
10809            false,
10810            fq,
10811        )
10812    }
10813
10814    fn visit_macro(&mut self, ancestry: &ParentIndex<'_>, node: Node<'_>) {
10815        // A comment can terminate tree-sitter's preproc_arg before the logical
10816        // directive ends. Its remaining declarations are replacement locals,
10817        // not file-scope fields, even when recovery exposes them as siblings.
10818        if let Some(replacement) =
10819            crate::graph::syntax::function_macro_replacement_span(node, self.source)
10820        {
10821            self.consumed_fragment_regions
10822                .push((replacement.start, replacement.end));
10823        }
10824        let Some(name) = extract_macro_name(node, self.source) else {
10825            return;
10826        };
10827        let signature = node_text(node, self.source).trim_end().to_string();
10828        if signature.is_empty() {
10829            return;
10830        }
10831        let fq = cpp_member_fq("", &name);
10832        // A macro can be undefined and redefined later in the same file. Its
10833        // structured directive is part of the declaration identity so the
10834        // temporal environment can navigate to the definition active at a
10835        // reference instead of collapsing every spelling to the first range.
10836        // The same physical directive parsed through another C/C++ reading
10837        // still produces the same unit and remains deduplicated.
10838        let code_unit = CodeUnit::with_signature_and_fq(
10839            self.file.clone(),
10840            CodeUnitType::Macro,
10841            "",
10842            name.clone(),
10843            Some(signature.clone()),
10844            false,
10845            fq,
10846        );
10847        if !self.parsed.contains_declaration(&code_unit) {
10848            self.add_declaration(ancestry, code_unit.clone(), node, None, None);
10849            let name_range = node
10850                .child_by_field_name("name")
10851                .map(cpp_declaration_range)
10852                .unwrap_or_else(|| cpp_declaration_range(node));
10853            self.parsed
10854                .record_materialization(MaterializationRecord::GeneratedDeclaration {
10855                    site: cpp_declaration_range(node),
10856                    argument: name_range,
10857                    kind: GenerationKind::PreprocessorDefinition,
10858                    unit: code_unit.clone(),
10859                });
10860            self.parsed.add_signature(code_unit, signature);
10861        }
10862        if node.kind() == "preproc_def" {
10863            update_object_macro_field_environment(
10864                node,
10865                self.source,
10866                &mut self.object_macro_fields,
10867                &mut self.ambiguous_object_macro_fields,
10868            );
10869        } else {
10870            self.object_macro_fields.remove(&name);
10871            self.ambiguous_object_macro_fields.remove(&name);
10872        }
10873    }
10874
10875    /// Bare object-like field-list macros inside an otherwise well-formed
10876    /// aggregate remain identifier nodes in a field declaration. More than
10877    /// one adjacent invocation can be folded into the same declaration, so
10878    /// inspect all of its structured identifier nodes.
10879    fn visit_bare_object_macro_fields(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
10880        if !matches!(node.kind(), "declaration" | "field_declaration" | "ERROR") {
10881            return false;
10882        }
10883        // Claiming the declaration means its members are accounted for, which
10884        // is only true when there is an owner to materialize them into. A file
10885        // scope reaches this with a declaration that merely *contains* an
10886        // aggregate spelling an invocation (tree-sitter wraps libuv's
10887        // `struct uv_fs_poll_s { UV_HANDLE_FIELDS ... }` in a top-level
10888        // `field_declaration` under a malformed header), and consuming it there
10889        // would discard the aggregate itself (#2985).
10890        if scope.class_unit.is_none() {
10891            return false;
10892        }
10893        let macro_nodes =
10894            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
10895        for macro_node in &macro_nodes {
10896            let name = node_text(*macro_node, self.source).trim();
10897            let fields = object_macro_field_closure(&self.object_macro_fields, name);
10898            self.materialize_object_macro_fields(
10899                fields,
10900                cpp_declaration_range(*macro_node),
10901                *macro_node,
10902                scope,
10903            );
10904        }
10905        let Some(last) = macro_nodes.last() else {
10906            return false;
10907        };
10908        // Claiming the declaration accounts for every member it spells, not
10909        // only the donated ones: an invocation folded together with the
10910        // members after it (libuv's `UV_HANDLE_FIELDS ... void* poll_ctx;`)
10911        // leaves those members shapeless, and the grammar recovers them from
10912        // the byte range that follows the invocation.
10913        self.record_collapsed_aggregate_fields(
10914            last.end_byte()..node.end_byte(),
10915            node.start_position().row
10916                + 1
10917                + cpp_line_breaks_between(self.source, node.start_byte(), last.end_byte()),
10918            node,
10919            scope,
10920        );
10921        true
10922    }
10923
10924    /// Retain the grammar's field interpretation with the original invocation
10925    /// or aggregate context. The replacement AST owns the type, while only the
10926    /// original source owns activation, guards, and occurrence ranges.
10927    fn capture_recovered_field_facts(
10928        &mut self,
10929        unit: &CodeUnit,
10930        range: Range,
10931        context: Node<'_>,
10932        field_type: Option<brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact>,
10933        names_function_type: bool,
10934    ) {
10935        let declaration = *self.source_links[unit]
10936            .last()
10937            .expect("recovered field follows its source occurrence");
10938        let mut fact = crate::source_facts::capture_declaration(
10939            declaration,
10940            context,
10941            unit,
10942            self.source,
10943            self.primary_ancestry,
10944        );
10945        fact.field_type = field_type;
10946        fact.names_function_type = names_function_type;
10947        fact.occurrence_role = brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Both;
10948        let captured = crate::source_context::capture_declaration_context(
10949            context,
10950            unit,
10951            self.source,
10952            &mut self.preprocessor_context_cache,
10953        );
10954        fact.guard_requirements = captured.guard_requirements;
10955        fact.callable_guards = fact.guard_requirements.clone();
10956        fact.callable_activation = Some(range.end_byte);
10957        fact.callable_guard_completion_byte = captured.callable_guard_completion_byte;
10958        fact.flattened_macro_namespace = captured.flattened_macro_namespace;
10959        let intern = |node: Node<'_>| {
10960            let mut collector = self.source_collector.borrow_mut();
10961            if node.id() == self.primary_root_id || self.primary_ancestry.contains(node) {
10962                collector.intern_node(node)
10963            } else {
10964                collector.intern_embedded(brokk_bifrost_core::analyzer::tree_walk::node_range(node))
10965            }
10966        };
10967        fact.conditional_family =
10968            crate::graph::resolver::preprocessor_conditional_family_node(context).map(intern);
10969        fact.exhaustive_conditional_family = captured.exhaustive_conditional_family.map(intern);
10970        fact.displaced_namespace_closing_brace =
10971            captured.displaced_namespace_closing_brace.map(intern);
10972        self.capture_source_dependencies(&mut fact, context, unit);
10973        self.apply_active_source_context(&mut fact);
10974        assert!(
10975            self.source_properties
10976                .borrow_mut()
10977                .insert(declaration, fact)
10978                .is_none()
10979        );
10980    }
10981
10982    fn materialize_object_macro_fields(
10983        &mut self,
10984        fields: Vec<MacroReplacementField>,
10985        range: Range,
10986        context: Node<'_>,
10987        scope: &ScopeInfo,
10988    ) {
10989        let Some(owner) = scope.class_unit.as_ref() else {
10990            return;
10991        };
10992        for field in fields {
10993            let signature = field.declaration.clone();
10994            let mut fq = owner.fq().clone();
10995            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
10996            let short_name = if owner.short_name().is_empty() {
10997                field.name.clone()
10998            } else {
10999                format!("{}.{}", owner.short_name(), field.name)
11000            };
11001            let code_unit = CodeUnit::with_signature_and_fq(
11002                self.file.clone(),
11003                CodeUnitType::Field,
11004                owner.package_name().to_string(),
11005                short_name,
11006                Some(field.declaration),
11007                true,
11008                fq,
11009            );
11010            if self.parsed.contains_declaration(&code_unit) {
11011                continue;
11012            }
11013            self.add_declaration_with_range(
11014                self.primary_ancestry,
11015                code_unit.clone(),
11016                range,
11017                None,
11018                Some(owner.clone()),
11019                None,
11020            );
11021            self.capture_recovered_field_facts(
11022                &code_unit,
11023                range,
11024                context,
11025                field.field_type,
11026                field.names_function_type,
11027            );
11028            self.parsed.add_signature(code_unit, signature);
11029        }
11030    }
11031
11032    fn collapsed_aggregate_owner(
11033        &self,
11034        name: &str,
11035        scope: &ScopeInfo,
11036        parent: Option<&CodeUnit>,
11037    ) -> CodeUnit {
11038        let short_name = parent.map_or_else(
11039            || name.to_string(),
11040            |parent| cpp_join_nested_short(parent.short_name(), name),
11041        );
11042        let fq = cpp_leaf_fq(
11043            &scope.package_name,
11044            parent,
11045            name,
11046            SegmentKind::Nested,
11047            SegmentKind::Type,
11048        );
11049        CodeUnit::with_signature_and_fq(
11050            self.file.clone(),
11051            CodeUnitType::Class,
11052            scope.package_name.clone(),
11053            short_name,
11054            None,
11055            false,
11056            fq,
11057        )
11058    }
11059
11060    /// Recover ordinary aggregates whose field-list macro invocations made
11061    /// tree-sitter collapse the class heads and bodies into one `ERROR` node.
11062    /// The class markers, aggregate braces, field nodes, and macro identifier
11063    /// nodes are all read from the AST; no source delimiter or token scan is
11064    /// used. A close-brace node inside a malformed field closes the innermost
11065    /// recovered aggregate, preserving nested-owner identity.
11066    fn visit_object_macro_error_classes(&mut self, node: Node<'_>, scope: &ScopeInfo) {
11067        let mut cursor = node.walk();
11068        let children = node.children(&mut cursor).collect::<Vec<_>>();
11069        let mut recovered = Vec::<(CodeUnit, usize, usize, Vec<CppCollapsedMember<'_>>)>::new();
11070        let mut object_macro_fields = self.object_macro_fields.clone();
11071        let mut ambiguous_object_macro_fields = self.ambiguous_object_macro_fields.clone();
11072        let mut open = Vec::<usize>::new();
11073        let mut index = 0;
11074        while index < children.len() {
11075            let keyword = children[index];
11076            if update_object_macro_field_environment(
11077                keyword,
11078                self.source,
11079                &mut object_macro_fields,
11080                &mut ambiguous_object_macro_fields,
11081            ) {
11082                index += 1;
11083                continue;
11084            }
11085            if let Some(head) = cpp_collapsed_aggregate_head(&children, index, self.source) {
11086                let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11087                if !name.is_empty() {
11088                    let parent = open
11089                        .last()
11090                        .and_then(|class| recovered.get(*class))
11091                        .map(|(owner, _, _, _)| owner.clone())
11092                        .or_else(|| scope.class_unit.clone());
11093                    let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11094                    recovered.push((
11095                        owner,
11096                        head.key.start_byte(),
11097                        head.opening.end_byte(),
11098                        Vec::new(),
11099                    ));
11100                    let class_index = recovered.len() - 1;
11101                    match head.folded_members {
11102                        // The ordinary shape leaves the aggregate open: every
11103                        // child that follows belongs to it until a close brace
11104                        // ends it.
11105                        None => open.push(class_index),
11106                        // A folded head carries its own member list and its own
11107                        // closing brace, so it closes here. The invocations that
11108                        // precede the class key are the dangling tail of the
11109                        // replacement that folded it, and belong to whatever
11110                        // aggregate is open around it, not to this one.
11111                        Some(members) => {
11112                            let macro_nodes = object_macro_identifier_nodes_with_environment(
11113                                children[index],
11114                                self.source,
11115                                &mut object_macro_fields,
11116                                &mut ambiguous_object_macro_fields,
11117                            );
11118                            let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11119                                .iter()
11120                                .partition(|node| node.start_byte() < head.key.start_byte());
11121                            if let Some(&enclosing) = open.last() {
11122                                for macro_node in preceding {
11123                                    recovered[enclosing]
11124                                        .3
11125                                        .push(CppCollapsedMember::MacroFields {
11126                                            context: macro_node,
11127                                            range: cpp_declaration_range(macro_node),
11128                                            fields: object_macro_field_closure(
11129                                                &object_macro_fields,
11130                                                &normalize_cpp_whitespace(node_text(
11131                                                    macro_node,
11132                                                    self.source,
11133                                                )),
11134                                            ),
11135                                        });
11136                                }
11137                            }
11138                            let closing = cpp_collapsed_aggregate_closing_brace(members);
11139                            recovered[class_index]
11140                                .3
11141                                .extend(cpp_collapsed_aggregate_members(
11142                                    members,
11143                                    &inner,
11144                                    head.opening.end_byte()..closing,
11145                                    head.opening.end_position().row + 1,
11146                                    self.source,
11147                                    &object_macro_fields,
11148                                ));
11149                            recovered[class_index].2 = members.end_byte();
11150                            for &open_class in &open {
11151                                recovered[open_class].2 =
11152                                    recovered[open_class].2.max(members.end_byte());
11153                            }
11154                        }
11155                    }
11156                    index += head.width;
11157                    continue;
11158                }
11159            }
11160            if let Some(&class_index) = open.last()
11161                && children[index].kind() == "field_declaration"
11162            {
11163                let field = children[index];
11164                let macro_nodes = object_macro_identifier_nodes_with_environment(
11165                    field,
11166                    self.source,
11167                    &mut object_macro_fields,
11168                    &mut ambiguous_object_macro_fields,
11169                );
11170                recovered[class_index]
11171                    .3
11172                    .extend(cpp_collapsed_aggregate_members(
11173                        field,
11174                        &macro_nodes,
11175                        field.start_byte()..field.end_byte(),
11176                        field.start_position().row + 1,
11177                        self.source,
11178                        &object_macro_fields,
11179                    ));
11180                let end = field.end_byte();
11181                for &open_class in &open {
11182                    recovered[open_class].2 = recovered[open_class].2.max(end);
11183                }
11184                let closes = count_close_brace_nodes(field);
11185                for _ in 0..closes {
11186                    if let Some(closed) = open.pop() {
11187                        recovered[closed].2 = end;
11188                    }
11189                }
11190            }
11191            index += 1;
11192        }
11193
11194        let mut owners = Vec::with_capacity(recovered.len());
11195        for (owner, start, end, members) in recovered {
11196            let parent = owners
11197                .iter()
11198                .find(|parent: &&CodeUnit| owner.fq().parent().as_ref() == Some(parent.fq()))
11199                .cloned()
11200                .or_else(|| scope.class_unit.clone());
11201            self.declare_collapsed_aggregate(
11202                owner.clone(),
11203                start..end,
11204                Some(node),
11205                parent,
11206                members,
11207                scope,
11208            );
11209            owners.push(owner);
11210        }
11211    }
11212
11213    /// Record one recovered aggregate and the members it declares.
11214    fn declare_collapsed_aggregate(
11215        &mut self,
11216        owner: CodeUnit,
11217        span: std::ops::Range<usize>,
11218        evidence: Option<Node<'_>>,
11219        parent: Option<CodeUnit>,
11220        members: Vec<CppCollapsedMember<'_>>,
11221        scope: &ScopeInfo,
11222    ) {
11223        let range = Range {
11224            start_byte: span.start,
11225            end_byte: span.end,
11226            start_line: self.source.get(..span.start).map_or(1, |source| {
11227                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11228            }),
11229            end_line: self.source.get(..span.end).map_or(1, |source| {
11230                source.bytes().filter(|byte| *byte == b'\n').count() + 1
11231            }),
11232        };
11233        // The tag can already be declared by a typedef or a forward
11234        // declaration, and this range is the definition's. Record it either
11235        // way: a declaration carries every range it is written at, and
11236        // dropping this one leaves nothing able to prove that the aggregate
11237        // has a body (#3098). Recording a range that is already held, or a
11238        // unit that is already declared, changes nothing.
11239        self.add_declaration_with_range(
11240            self.primary_ancestry,
11241            owner.clone(),
11242            range,
11243            evidence,
11244            parent,
11245            None,
11246        );
11247        // The recovery admitted this owner from an aggregate head and body.
11248        // Its malformed outer node need not itself be a struct_specifier, so
11249        // retain that structured definition proof explicitly.
11250        let declaration = *self.source_links[&owner]
11251            .last()
11252            .expect("collapsed aggregate has a source occurrence");
11253        {
11254            let mut properties = self.source_properties.borrow_mut();
11255            let fact = properties
11256                .get_mut(&declaration)
11257                .expect("collapsed aggregate retains its original context");
11258            fact.class_strength =
11259                brokk_bifrost_core::analyzer::cpp_facts::CppClassDeclarationStrength::Full;
11260            fact.occurrence_role =
11261                brokk_bifrost_core::analyzer::cpp_facts::CppOccurrenceRole::Definition;
11262        }
11263        let owner_scope = ScopeInfo {
11264            class_unit: Some(owner),
11265            declarations_are_fields: true,
11266            ..scope.clone()
11267        };
11268        for member in members {
11269            match member {
11270                CppCollapsedMember::MacroFields {
11271                    range,
11272                    fields,
11273                    context,
11274                } => {
11275                    self.materialize_object_macro_fields(fields, range, context, &owner_scope);
11276                }
11277                CppCollapsedMember::Declarations {
11278                    span,
11279                    start_line,
11280                    context,
11281                } => {
11282                    self.record_collapsed_aggregate_fields(span, start_line, context, &owner_scope);
11283                }
11284            }
11285        }
11286    }
11287
11288    /// Recover an aggregate whose head tree-sitter folded into the declaration
11289    /// that precedes it, outside any collapsed `ERROR` container.
11290    ///
11291    /// A comment inside a field-list macro's replacement ends the replacement
11292    /// token, so the rest of the replacement is read at file scope. When the
11293    /// aggregate that follows declares its member list with one invocation,
11294    /// the parser reads the dangling tail as that declaration's type, the
11295    /// class key as an `ERROR`, and the member list as an initializer (#3098).
11296    /// The invocations before the class key belong to the replacement, not to
11297    /// this aggregate, so they go to whatever aggregate encloses it.
11298    fn visit_folded_aggregate(&mut self, node: Node<'_>, scope: &ScopeInfo) -> bool {
11299        if node.parent().is_some_and(|parent| parent.is_error()) {
11300            // The container's own recovery reads this child in source order,
11301            // with the preprocessor environment its earlier children build.
11302            return false;
11303        }
11304        let Some(head) = cpp_folded_aggregate_head(node, self.source) else {
11305            return false;
11306        };
11307        let members = head
11308            .folded_members
11309            .expect("a folded aggregate head carries its member list");
11310        let name = normalize_cpp_whitespace(node_text(head.name, self.source));
11311        if name.is_empty() {
11312            return false;
11313        }
11314        let macro_nodes =
11315            object_macro_identifier_nodes(node, self.source, &self.object_macro_fields);
11316        let (preceding, inner): (Vec<_>, Vec<_>) = macro_nodes
11317            .iter()
11318            .partition(|macro_node| macro_node.start_byte() < head.key.start_byte());
11319        for macro_node in preceding {
11320            let fields = object_macro_field_closure(
11321                &self.object_macro_fields,
11322                &normalize_cpp_whitespace(node_text(macro_node, self.source)),
11323            );
11324            self.materialize_object_macro_fields(
11325                fields,
11326                cpp_declaration_range(macro_node),
11327                macro_node,
11328                scope,
11329            );
11330        }
11331        let parent = scope.class_unit.clone();
11332        let owner = self.collapsed_aggregate_owner(&name, scope, parent.as_ref());
11333        let recovered = cpp_collapsed_aggregate_members(
11334            members,
11335            &inner,
11336            head.opening.end_byte()..cpp_collapsed_aggregate_closing_brace(members),
11337            head.opening.end_position().row + 1,
11338            self.source,
11339            &self.object_macro_fields,
11340        );
11341        self.declare_collapsed_aggregate(
11342            owner,
11343            head.key.start_byte()..members.end_byte(),
11344            Some(node),
11345            parent,
11346            recovered,
11347            scope,
11348        );
11349        true
11350    }
11351
11352    /// Record the members of a collapsed aggregate region as fields of its
11353    /// recovered owner. The names, declaration text, and byte ranges all come
11354    /// from the grammar's reading of the region, so a region that is not a
11355    /// member list contributes nothing.
11356    fn record_collapsed_aggregate_fields(
11357        &mut self,
11358        span: std::ops::Range<usize>,
11359        start_line: usize,
11360        context: Node<'_>,
11361        scope: &ScopeInfo,
11362    ) {
11363        let Some(owner) = scope.class_unit.as_ref() else {
11364            return;
11365        };
11366        for field in crate::graph::syntax::recovered_aggregate_fields(self.source, span.clone()) {
11367            let range = Range {
11368                start_byte: field.range.start,
11369                end_byte: field.range.end,
11370                start_line: start_line
11371                    + cpp_line_breaks_between(self.source, span.start, field.range.start),
11372                end_line: start_line
11373                    + cpp_line_breaks_between(self.source, span.start, field.range.end),
11374            };
11375            let mut fq = owner.fq().clone();
11376            fq.push(segment_interner().intern(&field.name, SegmentKind::Member));
11377            let short_name = if owner.short_name().is_empty() {
11378                field.name.clone()
11379            } else {
11380                format!("{}.{}", owner.short_name(), field.name)
11381            };
11382            let signature = normalize_cpp_whitespace(&field.declaration);
11383            let code_unit = CodeUnit::with_signature_and_fq(
11384                self.file.clone(),
11385                CodeUnitType::Field,
11386                owner.package_name().to_string(),
11387                short_name,
11388                Some(signature.clone()),
11389                false,
11390                fq,
11391            );
11392            if self.parsed.contains_declaration(&code_unit) {
11393                continue;
11394            }
11395            self.add_declaration_with_range(
11396                self.primary_ancestry,
11397                code_unit.clone(),
11398                range,
11399                None,
11400                Some(owner.clone()),
11401                None,
11402            );
11403            self.capture_recovered_field_facts(
11404                &code_unit,
11405                range,
11406                context,
11407                field.field_type,
11408                field.names_function_type,
11409            );
11410            self.parsed.add_signature(code_unit, signature);
11411        }
11412    }
11413
11414    fn visit_preproc_call(&mut self, node: Node<'_>) {
11415        let Some(_directive) = node.child_by_field_name("directive") else {
11416            return;
11417        };
11418        if is_cpp_undef_directive(node, self.source) {
11419            update_object_macro_field_environment(
11420                node,
11421                self.source,
11422                &mut self.object_macro_fields,
11423                &mut self.ambiguous_object_macro_fields,
11424            );
11425        }
11426    }
11427}
11428
11429/// Every member a field-list macro contributes under `environment`, including
11430/// the members of the field-list macros it composes.
11431///
11432/// Composition is resolved at the invocation, not at the definition, because a
11433/// nested name's active replacement is a property of the environment the
11434/// invocation sees. The traversal is iterative and visits each name once, so a
11435/// self-composing or mutually composing pair terminates.
11436fn object_macro_field_closure(
11437    environment: &HashMap<String, ObjectMacroReplacement>,
11438    name: &str,
11439) -> Vec<MacroReplacementField> {
11440    let mut fields = Vec::new();
11441    let mut visited = HashSet::default();
11442    let mut stack = vec![name.to_string()];
11443    while let Some(current) = stack.pop() {
11444        if !visited.insert(current.clone()) {
11445            continue;
11446        }
11447        let Some(replacement) = environment.get(&current) else {
11448            continue;
11449        };
11450        fields.extend(replacement.fields.iter().cloned());
11451        stack.extend(replacement.nested.iter().rev().cloned());
11452    }
11453    fields
11454}
11455
11456/// The structured replacement an object-like `#define` contributes.
11457///
11458/// The directive's whole logical line is the replacement list, which
11459/// [`object_macro_replacement_span`] recovers, because a comment inside the
11460/// replacement ends tree-sitter's `preproc_arg` token early.
11461fn object_macro_replacement_of(node: Node<'_>, source: &str) -> ObjectMacroReplacement {
11462    crate::graph::syntax::object_macro_replacement_span(node, source)
11463        .and_then(|span| source.get(span))
11464        .map(crate::graph::syntax::object_macro_replacement)
11465        .unwrap_or_default()
11466}
11467
11468/// Apply one preprocessor directive to an object-like field-list environment.
11469/// The environment is intentionally separate from the declaration visitor so
11470/// malformed parser regions can replay directives in source order without
11471/// mutating the live walk's state ahead of those directives.
11472fn update_object_macro_field_environment(
11473    node: Node<'_>,
11474    source: &str,
11475    fields: &mut HashMap<String, ObjectMacroReplacement>,
11476    ambiguous: &mut HashSet<String>,
11477) -> bool {
11478    match node.kind() {
11479        "preproc_def" => {
11480            let Some(name) = extract_macro_name(node, source) else {
11481                return false;
11482            };
11483            let replacement = object_macro_replacement_of(node, source);
11484            if replacement.is_empty() || ambiguous.contains(&name) {
11485                fields.remove(&name);
11486                ambiguous.insert(name);
11487            } else if let Some(previous) = fields.get(&name) {
11488                if previous != &replacement {
11489                    fields.remove(&name);
11490                    ambiguous.insert(name);
11491                }
11492            } else {
11493                fields.insert(name, replacement);
11494            }
11495            true
11496        }
11497        "preproc_call" if is_cpp_undef_directive(node, source) => {
11498            if let Some(argument) = node.child_by_field_name("argument") {
11499                let name = node_text(argument, source).trim();
11500                fields.remove(name);
11501                if inside_preprocessor_conditional(node) {
11502                    ambiguous.insert(name.to_string());
11503                } else {
11504                    ambiguous.remove(name);
11505                }
11506            }
11507            true
11508        }
11509        _ => false,
11510    }
11511}
11512
11513fn object_macro_identifier_nodes<'tree>(
11514    node: Node<'tree>,
11515    source: &str,
11516    fields: &HashMap<String, ObjectMacroReplacement>,
11517) -> Vec<Node<'tree>> {
11518    walk_object_macro_identifier_nodes(node, |current, result| {
11519        if matches!(
11520            current.kind(),
11521            "identifier" | "field_identifier" | "type_identifier"
11522        ) && fields.contains_key(node_text(current, source).trim())
11523        {
11524            result.push(current);
11525        }
11526        false
11527    })
11528}
11529
11530fn walk_object_macro_identifier_nodes<'tree>(
11531    node: Node<'tree>,
11532    mut visit: impl FnMut(Node<'tree>, &mut Vec<Node<'tree>>) -> bool,
11533) -> Vec<Node<'tree>> {
11534    let mut result = Vec::new();
11535    let mut stack = vec![node];
11536    while let Some(current) = stack.pop() {
11537        if visit(current, &mut result) {
11538            continue;
11539        }
11540        let mut cursor = current.walk();
11541        let children = current.children(&mut cursor).collect::<Vec<_>>();
11542        stack.extend(children.into_iter().rev());
11543    }
11544    result.sort_by_key(|node| node.start_byte());
11545    result
11546}
11547
11548/// Collect macro identifiers while replaying any nested preprocessor
11549/// directives in source order. Recovery regions can contain a later `#undef`
11550/// in the same parser error envelope; using the live visitor map for the
11551/// entire envelope would incorrectly materialize invocations after it.
11552fn object_macro_identifier_nodes_with_environment<'tree>(
11553    node: Node<'tree>,
11554    source: &str,
11555    fields: &mut HashMap<String, ObjectMacroReplacement>,
11556    ambiguous: &mut HashSet<String>,
11557) -> Vec<Node<'tree>> {
11558    walk_object_macro_identifier_nodes(node, |current, result| {
11559        if update_object_macro_field_environment(current, source, fields, ambiguous) {
11560            return true;
11561        }
11562        if matches!(
11563            current.kind(),
11564            "identifier" | "field_identifier" | "type_identifier"
11565        ) && fields.contains_key(node_text(current, source).trim())
11566        {
11567            result.push(current);
11568        }
11569        false
11570    })
11571}
11572
11573/// One member region of an aggregate whose head and body tree-sitter collapsed
11574/// into an `ERROR` container, in source order.
11575enum CppCollapsedMember<'tree> {
11576    /// The members a field-list macro invocation donates, all sharing the
11577    /// invocation's range.
11578    MacroFields {
11579        context: Node<'tree>,
11580        range: Range,
11581        fields: Vec<MacroReplacementField>,
11582    },
11583    /// A byte span whose ordinary member declarations lost their shape with
11584    /// the aggregate body, with the 1-based line the span starts on.
11585    Declarations {
11586        context: Node<'tree>,
11587        span: std::ops::Range<usize>,
11588        start_line: usize,
11589    },
11590}
11591
11592/// One aggregate head recovered from the children of a collapsed `ERROR`
11593/// container.
11594///
11595/// A member list that opens with an object-like field-list macro invocation
11596/// has no grammar rule, so the aggregate never forms a specifier and its head
11597/// reaches the tree as loose children. Two shapes carry it. Ordinarily the
11598/// class key, the name, and the opening brace are three consecutive children.
11599/// When the aggregate follows the dangling tail of a comment-truncated
11600/// replacement, the parser reads that tail as the declaration's type and folds
11601/// the whole aggregate into it: libuv's `UV_HANDLE_PRIVATE_FIELDS` line ends
11602/// the `UV_HANDLE_FIELDS` replacement token, and the `struct uv_handle_s { ...
11603/// }` that follows becomes one `field_declaration` whose class key is an
11604/// `ERROR`, whose name is a `field_identifier`, and whose member list is an
11605/// `initializer_list` (#3098).
11606struct CppCollapsedAggregateHead<'tree> {
11607    key: Node<'tree>,
11608    name: Node<'tree>,
11609    opening: Node<'tree>,
11610    /// The member list when the head folded it into its own node, which also
11611    /// carries the aggregate's closing brace. `None` leaves the aggregate open
11612    /// for the children that follow it.
11613    folded_members: Option<Node<'tree>>,
11614    /// How many children the head consumes.
11615    width: usize,
11616}
11617
11618/// The aggregate head that starts at `children[index]`, if one does.
11619fn cpp_collapsed_aggregate_head<'tree>(
11620    children: &[Node<'tree>],
11621    index: usize,
11622    source: &str,
11623) -> Option<CppCollapsedAggregateHead<'tree>> {
11624    let key = *children.get(index)?;
11625    if matches!(key.kind(), "struct" | "class" | "union") {
11626        let name = *children.get(index + 1)?;
11627        let opening = *children.get(index + 2)?;
11628        if !matches!(name.kind(), "type_identifier" | "identifier") || opening.kind() != "{" {
11629            return None;
11630        }
11631        return Some(CppCollapsedAggregateHead {
11632            key,
11633            name,
11634            opening,
11635            folded_members: None,
11636            width: 3,
11637        });
11638    }
11639    cpp_folded_aggregate_head(key, source)
11640}
11641
11642/// The aggregate head tree-sitter folded into one declaration node, reading
11643/// the class key as an `ERROR` and the member list as an initializer.
11644fn cpp_folded_aggregate_head<'tree>(
11645    node: Node<'tree>,
11646    source: &str,
11647) -> Option<CppCollapsedAggregateHead<'tree>> {
11648    if !matches!(node.kind(), "declaration" | "field_declaration") {
11649        return None;
11650    }
11651    let mut cursor = node.walk();
11652    let children = node.children(&mut cursor).collect::<Vec<_>>();
11653    let key_index = children.iter().position(|child| {
11654        child.is_error()
11655            && matches!(
11656                node_text(*child, source).trim(),
11657                "struct" | "class" | "union"
11658            )
11659    })?;
11660    let declarator = *children.get(key_index + 1)?;
11661    // At file scope the grammar has an `init_declarator` for the name and its
11662    // initializer; inside an `ERROR` container the two are loose siblings.
11663    let (name, members) = if declarator.kind() == "init_declarator" {
11664        (
11665            declarator.child_by_field_name("declarator")?,
11666            declarator.child_by_field_name("value")?,
11667        )
11668    } else {
11669        (declarator, *children.get(key_index + 2)?)
11670    };
11671    if !matches!(
11672        name.kind(),
11673        "field_identifier" | "type_identifier" | "identifier"
11674    ) {
11675        return None;
11676    }
11677    if members.kind() != "initializer_list" {
11678        return None;
11679    }
11680    let opening = members.child(0).filter(|brace| brace.kind() == "{")?;
11681    Some(CppCollapsedAggregateHead {
11682        key: children[key_index],
11683        name,
11684        opening,
11685        folded_members: Some(members),
11686        width: 1,
11687    })
11688}
11689
11690/// Where a folded member list ends: at its own closing brace when it has one,
11691/// so the reparse of the declarations it holds sees a member list and not a
11692/// stray brace.
11693fn cpp_collapsed_aggregate_closing_brace(members: Node<'_>) -> usize {
11694    let mut cursor = members.walk();
11695    members
11696        .children(&mut cursor)
11697        .filter(|child| child.kind() == "}" && !child.is_missing())
11698        .last()
11699        .map_or_else(|| members.end_byte(), |brace| brace.start_byte())
11700}
11701
11702/// The members one collapsed aggregate region declares, in source order: what
11703/// its field-list macro invocations donate, then the ordinary declarations
11704/// that follow the last invocation and lost their shape with the body.
11705fn cpp_collapsed_aggregate_members<'tree>(
11706    context: Node<'tree>,
11707    macro_nodes: &[Node<'tree>],
11708    region: std::ops::Range<usize>,
11709    region_start_line: usize,
11710    source: &str,
11711    environment: &HashMap<String, ObjectMacroReplacement>,
11712) -> Vec<CppCollapsedMember<'tree>> {
11713    let mut members = macro_nodes
11714        .iter()
11715        .map(|macro_node| CppCollapsedMember::MacroFields {
11716            context: *macro_node,
11717            range: cpp_declaration_range(*macro_node),
11718            fields: object_macro_field_closure(
11719                environment,
11720                &normalize_cpp_whitespace(node_text(*macro_node, source)),
11721            ),
11722        })
11723        .collect::<Vec<_>>();
11724    // Whatever follows the last invocation is an ordinary member list that
11725    // lost its declaration shape with the aggregate's body; the grammar
11726    // recovers it from its own byte range.
11727    let declarations_start = macro_nodes
11728        .last()
11729        .map_or(region.start, |macro_node| macro_node.end_byte());
11730    members.push(CppCollapsedMember::Declarations {
11731        context,
11732        span: declarations_start..region.end,
11733        start_line: region_start_line
11734            + cpp_line_breaks_between(source, region.start, declarations_start),
11735    });
11736    members
11737}
11738
11739/// Line breaks in `source[from..to]`, for mapping a recovered byte offset to a
11740/// line without rescanning the file from its start.
11741fn cpp_line_breaks_between(source: &str, from: usize, to: usize) -> usize {
11742    source.get(from..to).map_or(0, |slice| {
11743        slice.bytes().filter(|byte| *byte == b'\n').count()
11744    })
11745}
11746
11747/// How many enclosing aggregates a collapsed member declaration closes: the
11748/// close braces it carries that its own braces do not balance. A member that
11749/// declares a nested aggregate of its own (`union { ... } active_reqs;` in
11750/// libuv's `uv_loop_s`) closes nothing, so counting every `}` node ended the
11751/// owner early and lost every member after it (#2985).
11752fn count_close_brace_nodes(node: Node<'_>) -> usize {
11753    let mut opened = 0usize;
11754    let mut closed = 0usize;
11755    let mut stack = vec![node];
11756    while let Some(current) = stack.pop() {
11757        if !current.is_missing() {
11758            match current.kind() {
11759                "{" => opened += 1,
11760                "}" => closed += 1,
11761                _ => {}
11762            }
11763        }
11764        let mut cursor = current.walk();
11765        stack.extend(current.children(&mut cursor));
11766    }
11767    closed.saturating_sub(opened)
11768}
11769
11770/// Classify a C++ field while its declaration syntax is already available.
11771///
11772/// The persisted result lets later visibility queries avoid reparsing the
11773/// complete source file only to recover linkage.
11774pub fn cpp_field_declaration_linkage<'tree>(
11775    declaration: Node<'tree>,
11776    source: &str,
11777    ancestry: &ParentIndex<'tree>,
11778) -> CppFieldLinkage {
11779    let mut current = ancestry.parent(declaration);
11780    let mut enclosed_by_class = false;
11781    while let Some(node) = current {
11782        if node.kind() == "namespace_definition"
11783            && node
11784                .child_by_field_name("name")
11785                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11786        {
11787            return CppFieldLinkage::Internal;
11788        }
11789        if matches!(
11790            node.kind(),
11791            "class_specifier" | "struct_specifier" | "union_specifier"
11792        ) && node
11793            .child_by_field_name("name")
11794            .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
11795        {
11796            return CppFieldLinkage::Internal;
11797        }
11798        if matches!(
11799            node.kind(),
11800            "class_specifier" | "struct_specifier" | "union_specifier"
11801        ) {
11802            enclosed_by_class = true;
11803        }
11804        if matches!(node.kind(), "function_definition" | "lambda_expression") {
11805            return CppFieldLinkage::Internal;
11806        }
11807        current = ancestry.parent(node);
11808    }
11809    if enclosed_by_class {
11810        return CppFieldLinkage::External;
11811    }
11812    let mut cursor = declaration.walk();
11813    let mut has_static = false;
11814    let mut has_extern = false;
11815    let mut has_inline = false;
11816    let mut has_const = false;
11817    let mut has_constexpr = false;
11818    for child in declaration.named_children(&mut cursor) {
11819        let text = normalize_cpp_whitespace(node_text(child, source));
11820        match (child.kind(), text.as_str()) {
11821            ("storage_class_specifier", "static") => has_static = true,
11822            ("storage_class_specifier", "extern") => has_extern = true,
11823            ("storage_class_specifier", "inline") => has_inline = true,
11824            ("storage_class_specifier", "constexpr") => has_constexpr = true,
11825            ("type_qualifier", "const") => has_const = true,
11826            ("type_qualifier", "constexpr") => has_constexpr = true,
11827            _ => {}
11828        }
11829    }
11830    if has_static {
11831        CppFieldLinkage::Internal
11832    } else if has_extern || has_inline {
11833        CppFieldLinkage::External
11834    } else if has_const || has_constexpr {
11835        CppFieldLinkage::InternalUnlessExternalPeer
11836    } else {
11837        CppFieldLinkage::External
11838    }
11839}
11840
11841fn cpp_declaration_range(node: Node<'_>) -> Range {
11842    Range {
11843        start_byte: node.start_byte(),
11844        end_byte: node.end_byte(),
11845        start_line: node.start_position().row + 1,
11846        end_line: node.end_position().row + 1,
11847    }
11848}
11849
11850/// A recovery interval as a [`Range`], for materialization records whose
11851/// window is a byte region rather than one parser node (the sentinel-macro
11852/// region reparses, issue #941/#1657).
11853fn cpp_recovery_window(source: &str, start_byte: usize, end_byte: usize) -> Range {
11854    let line_at = |byte: usize| {
11855        source.as_bytes()[..byte]
11856            .iter()
11857            .filter(|&&b| b == b'\n')
11858            .count()
11859            + 1
11860    };
11861    Range {
11862        start_byte,
11863        end_byte,
11864        start_line: line_at(start_byte),
11865        end_line: line_at(end_byte),
11866    }
11867}
11868
11869fn cpp_include_info(raw_snippet: String) -> ImportInfo {
11870    ImportInfo {
11871        raw_snippet,
11872        is_wildcard: false,
11873        is_global: false,
11874        identifier: None,
11875        alias: None,
11876        path: None,
11877        binder_span: None,
11878    }
11879}
11880
11881fn recover_quoted_source_imports(
11882    source: &str,
11883    imports: &mut Vec<SourceImportFact>,
11884    includes: &mut Vec<brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact>,
11885    collector: &RefCell<PrimarySourceFactCollector<'_>>,
11886) {
11887    let mut in_block_comment = false;
11888    let mut start = 0;
11889    for line in source.split_inclusive('\n') {
11890        let end = start + line.len();
11891        let stripped = strip_cpp_comments_from_line(line, &mut in_block_comment);
11892        let trimmed = stripped.trim();
11893        if looks_like_quoted_include_line(trimmed) {
11894            let raw = normalize_cpp_whitespace(trimmed);
11895            if !imports.iter().any(|import| import.statement == raw) {
11896                let occurrence = collector.borrow_mut().intern_subspan_bytes(
11897                    start,
11898                    end,
11899                    SourceOccurrenceProvenance::ExplicitSubspan,
11900                );
11901                // Recovery already identified a quoted directive hidden by a
11902                // malformed outer construct. Interpret its path with the same
11903                // grammar in an isolated included range, preserving byte identity.
11904                let region =
11905                    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end);
11906                let target = region.as_ref().and_then(|tree| {
11907                    let mut pending = vec![tree.root_node()];
11908                    while let Some(node) = pending.pop() {
11909                        if node.kind() == "preproc_include"
11910                            && let Some(path) = node.child_by_field_name("path")
11911                            && path.kind() == "string_literal"
11912                        {
11913                            let (value, _) = crate::imports::include_path_from_node(node, source)?;
11914                            let target = collector.borrow_mut().intern_embedded(
11915                                brokk_bifrost_core::analyzer::tree_walk::node_range(path),
11916                            );
11917                            includes.push(
11918                                brokk_bifrost_core::analyzer::cpp_facts::CppIncludeFact {
11919                                    declaration: occurrence,
11920                                    target,
11921                                    path: value.to_owned(),
11922                                    quoted: true,
11923                                },
11924                            );
11925                            return Some(target);
11926                        }
11927                        let mut cursor = node.walk();
11928                        let children: Vec<_> = node.named_children(&mut cursor).collect();
11929                        pending.extend(children.into_iter().rev());
11930                    }
11931                    None
11932                });
11933                imports.push(SourceImportFact::from_import(
11934                    cpp_include_info(raw),
11935                    occurrence,
11936                    target,
11937                    None,
11938                    Vec::new(),
11939                ));
11940            }
11941        }
11942        start = end;
11943    }
11944}
11945
11946fn looks_like_quoted_include_line(line: &str) -> bool {
11947    let Some(rest) = line.trim_start().strip_prefix('#') else {
11948        return false;
11949    };
11950    let Some(rest) = rest.trim_start().strip_prefix("include") else {
11951        return false;
11952    };
11953    rest.trim_start().starts_with('"')
11954}
11955
11956fn extract_cpp_supertypes(node: Node<'_>, source: &str) -> Vec<String> {
11957    let mut raw = Vec::new();
11958    if let Some(bases) = cpp_class_like_base_clause(node) {
11959        collect_cpp_base_nodes(bases, source, &mut raw);
11960    }
11961    raw
11962}
11963
11964fn extract_cpp_base_facts(node: Node<'_>, source: &str) -> Vec<CppBaseSpecifierFact> {
11965    let mut facts = Vec::new();
11966    let mut cursor = node.walk();
11967    for child in node.named_children(&mut cursor) {
11968        if child.kind() != "base_class_clause" {
11969            continue;
11970        }
11971        let mut is_virtual = false;
11972        let mut child_cursor = child.walk();
11973        for base in child.children(&mut child_cursor) {
11974            match base.kind() {
11975                "virtual" => is_virtual = true,
11976                "," => is_virtual = false,
11977                "type_identifier"
11978                | "qualified_identifier"
11979                | "scoped_type_identifier"
11980                | "template_type"
11981                | "template_function" => {
11982                    if let Some(fact) = crate::source_facts::base_specifier_from_node(
11983                        base,
11984                        source,
11985                        is_virtual,
11986                        crate::source_facts::base_access_specifier(child, base, node.kind()),
11987                    ) {
11988                        facts.push(fact);
11989                    }
11990                    is_virtual = false;
11991                }
11992                _ => {}
11993            }
11994        }
11995    }
11996    facts
11997}
11998
11999/// The `base_class_clause` a class-like node writes, if it writes one.
12000///
12001/// The pinned grammar exposes the clause as a named child rather than a field,
12002/// so the question is asked by kind. A base clause is the only place a C++
12003/// class body records that some other class may declare a member, which is
12004/// what [`cpp_callable_dispatch_extensibility`] reads it for.
12005fn cpp_class_like_base_clause<'tree>(class_like: Node<'tree>) -> Option<Node<'tree>> {
12006    let mut cursor = class_like.walk();
12007    class_like
12008        .named_children(&mut cursor)
12009        .find(|child| child.kind() == "base_class_clause")
12010}
12011
12012fn collect_cpp_base_nodes(node: Node<'_>, source: &str, raw: &mut Vec<String>) {
12013    walk_named_tree_preorder(node, false, |child| match child.kind() {
12014        "type_identifier" | "qualified_identifier" | "template_type" => {
12015            let text = normalize_cpp_whitespace(node_text(child, source));
12016            if !text.is_empty() {
12017                raw.push(text);
12018            }
12019            WalkControl::SkipChildren
12020        }
12021        _ => WalkControl::Continue,
12022    });
12023}
12024
12025fn strip_cpp_comments_from_line(line: &str, in_block_comment: &mut bool) -> String {
12026    let mut out = String::new();
12027    let chars: Vec<char> = line.chars().collect();
12028    let mut index = 0;
12029    let mut in_string = false;
12030    let mut in_char = false;
12031    let mut escape = false;
12032
12033    while index < chars.len() {
12034        let ch = chars[index];
12035        let next = chars.get(index + 1).copied();
12036
12037        if *in_block_comment {
12038            if ch == '*' && next == Some('/') {
12039                *in_block_comment = false;
12040                index += 2;
12041            } else {
12042                index += 1;
12043            }
12044            continue;
12045        }
12046
12047        if in_string {
12048            out.push(ch);
12049            if escape {
12050                escape = false;
12051            } else if ch == '\\' {
12052                escape = true;
12053            } else if ch == '"' {
12054                in_string = false;
12055            }
12056            index += 1;
12057            continue;
12058        }
12059
12060        if in_char {
12061            out.push(ch);
12062            if escape {
12063                escape = false;
12064            } else if ch == '\\' {
12065                escape = true;
12066            } else if ch == '\'' {
12067                in_char = false;
12068            }
12069            index += 1;
12070            continue;
12071        }
12072
12073        if ch == '/' && next == Some('/') {
12074            break;
12075        }
12076        if ch == '/' && next == Some('*') {
12077            *in_block_comment = true;
12078            index += 2;
12079            continue;
12080        }
12081        if ch == '"' {
12082            in_string = true;
12083            out.push(ch);
12084            index += 1;
12085            continue;
12086        }
12087        if ch == '\'' {
12088            in_char = true;
12089            out.push(ch);
12090            index += 1;
12091            continue;
12092        }
12093
12094        out.push(ch);
12095        index += 1;
12096    }
12097
12098    out
12099}
12100
12101#[derive(Clone)]
12102struct FunctionInfo {
12103    package_name: String,
12104    owner: Option<CppMemberOwner>,
12105    name: String,
12106    signature: String,
12107}
12108
12109/// Owner of a member function, kept structured so a literal `$` inside a
12110/// source-level class name never crosses a join/split boundary: the legacy
12111/// `$`-joined owner string was re-split at fq construction, dropping a leading
12112/// `$` (`$262Object` became `262Object` in the fq while short_name kept it)
12113/// and tripping the package/short boundary assert -- the #2140 corruption one
12114/// level up (#2362).
12115#[derive(Clone)]
12116enum CppMemberOwner {
12117    /// Source-level owner class chain from a qualified declarator-id, one
12118    /// class name per component (`Outer::Inner::method` -> `["Outer",
12119    /// "Inner"]`); each component may itself contain a literal `$`.
12120    Chain(Vec<String>),
12121    /// The lexically enclosing or recovered class unit; the member fq extends
12122    /// its fq directly instead of re-splitting its `$`-joined short chain.
12123    Unit(CodeUnit),
12124}
12125
12126impl CppMemberOwner {
12127    /// The legacy `$`-joined owner chain used in the member's short name.
12128    fn short_chain(&self) -> String {
12129        match self {
12130            Self::Chain(chain) => chain.join("$"),
12131            Self::Unit(parent) => parent.short_name().to_string(),
12132        }
12133    }
12134}
12135
12136enum DeclaratorKind<'a> {
12137    Function(Node<'a>),
12138    Variable(Node<'a>),
12139}
12140
12141impl FunctionInfo {
12142    fn code_unit(&self, file: ProjectFile) -> CodeUnit {
12143        self.code_unit_with_synthetic(file, false)
12144    }
12145
12146    fn code_unit_with_synthetic(&self, file: ProjectFile, synthetic: bool) -> CodeUnit {
12147        let short_name = match &self.owner {
12148            Some(owner) => cpp_join_member_short(&owner.short_chain(), &self.name),
12149            None => self.name.clone(),
12150        };
12151        let fq = match &self.owner {
12152            Some(CppMemberOwner::Chain(chain)) => {
12153                debug_assert!(
12154                    !chain.is_empty(),
12155                    "an empty owner chain is no owner; producers return None instead"
12156                );
12157                let mut fq = FqName::new();
12158                cpp_push_package(&mut fq, &self.package_name);
12159                let mut first = true;
12160                for component in chain {
12161                    let kind = if first {
12162                        SegmentKind::Type
12163                    } else {
12164                        SegmentKind::Nested
12165                    };
12166                    fq.push(segment_interner().intern(component, kind));
12167                    first = false;
12168                }
12169                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12170                fq
12171            }
12172            Some(CppMemberOwner::Unit(parent)) if !parent.short_name().is_empty() => parent
12173                .fq()
12174                .clone()
12175                .with_pushed(segment_interner().intern(&self.name, SegmentKind::Member)),
12176            // An anonymous parent (empty short chain) contributes no owner
12177            // segment -- the same guard as cpp_join_member_short above.
12178            Some(CppMemberOwner::Unit(_)) | None => {
12179                let mut fq = FqName::new();
12180                cpp_push_package(&mut fq, &self.package_name);
12181                fq.push(segment_interner().intern(&self.name, SegmentKind::Member));
12182                fq
12183            }
12184        };
12185        CodeUnit::with_signature_and_fq(
12186            file,
12187            CodeUnitType::Function,
12188            self.package_name.clone(),
12189            short_name,
12190            Some(self.signature.clone()),
12191            synthetic,
12192            fq,
12193        )
12194    }
12195}
12196
12197fn extract_function_info(
12198    declarator: Node<'_>,
12199    source: &str,
12200    scope: &ScopeInfo,
12201) -> Option<FunctionInfo> {
12202    let parameters_node = declarator.child_by_field_name("parameters")?;
12203    let declarator_name_node = declarator
12204        .child_by_field_name("declarator")
12205        .or_else(|| parameters_node.prev_named_sibling())?;
12206    extract_function_info_from_name(declarator, declarator_name_node, source, scope)
12207}
12208
12209fn extract_function_info_from_name(
12210    declarator: Node<'_>,
12211    declarator_name_node: Node<'_>,
12212    source: &str,
12213    scope: &ScopeInfo,
12214) -> Option<FunctionInfo> {
12215    let parameters_node = declarator.child_by_field_name("parameters")?;
12216    let parameters_text = cpp_parameter_signature(parameters_node, source);
12217    let recovered_specialization_member = scope
12218        .recovered_specialization_member_scope
12219        .then(|| {
12220            let terminal = declarator_name_node
12221                .child_by_field_name("name")
12222                .unwrap_or(declarator_name_node);
12223            let name = canonical_cpp_qualified_component(terminal, source)?.name;
12224            let owner = scope.class_unit.as_ref()?;
12225            Some((
12226                Some(CppMemberOwner::Unit(owner.clone())),
12227                name,
12228                scope.package_name.clone(),
12229            ))
12230        })
12231        .flatten();
12232    let (owner, name, package_name) = if let Some(parts) = recovered_specialization_member {
12233        parts
12234    } else if let Some(parts) =
12235        split_structured_templated_cpp_name(declarator, declarator_name_node, source, scope)
12236    {
12237        parts
12238    } else {
12239        let raw_name = normalize_cpp_whitespace(&extract_callable_declarator_name(
12240            declarator_name_node,
12241            source,
12242        )?);
12243        if raw_name.is_empty() {
12244            return None;
12245        }
12246        let (owner, name, package_name) = split_cpp_name(&raw_name, scope);
12247        match using_bound_cpp_owner(declarator_name_node, source, scope) {
12248            Some((owner_chain, package_name)) => {
12249                (Some(CppMemberOwner::Chain(owner_chain)), name, package_name)
12250            }
12251            None => (owner, name, package_name),
12252        }
12253    };
12254    let suffix = cpp_declarator_identity_suffix(declarator, parameters_node, source);
12255    let mut signature = if suffix.is_empty() {
12256        parameters_text
12257    } else {
12258        format!("{parameters_text} {suffix}")
12259    };
12260    if let Some(template_signature) = &scope.template_signature {
12261        signature = format!("{template_signature}{signature}");
12262    }
12263
12264    Some(FunctionInfo {
12265        package_name,
12266        owner,
12267        name,
12268        signature,
12269    })
12270}
12271
12272/// Recover the semantic return type and callable name when a declaration macro
12273/// occupies a function definition's `type` field. Tree-sitter either exposes a
12274/// scalar return as the declarator's apparent name and the callable as the sole
12275/// identifier in an `ERROR`, or joins a template return and callable into a
12276/// qualified identifier with a missing `::`. Both shapes retain the complete
12277/// parameter list and body; a concrete separator remains an out-of-line member.
12278fn cpp_macro_displaced_callable_parts<'tree>(
12279    function_declarator: Node<'tree>,
12280    source: &str,
12281    ancestry: &ParentIndex<'tree>,
12282) -> Option<(Node<'tree>, Node<'tree>)> {
12283    let definition = ancestry.parent(function_declarator)?;
12284    if definition.kind() != "function_definition"
12285        || definition.child_by_field_name("declarator") != Some(function_declarator)
12286        || definition
12287            .child_by_field_name("body")
12288            .is_none_or(|body| body.kind() != "compound_statement")
12289    {
12290        return None;
12291    }
12292    let macro_type = definition.child_by_field_name("type")?;
12293    if macro_type.kind() != "type_identifier"
12294        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
12295    {
12296        return None;
12297    }
12298
12299    let apparent_return_type = function_declarator.child_by_field_name("declarator")?;
12300    if apparent_return_type.kind() == "qualified_identifier"
12301        && let (Some(return_type), Some(callable_name)) = (
12302            apparent_return_type.child_by_field_name("scope"),
12303            apparent_return_type.child_by_field_name("name"),
12304        )
12305        && return_type.kind() == "template_type"
12306        && matches!(callable_name.kind(), "identifier" | "field_identifier")
12307        && (0..apparent_return_type.child_count())
12308            .filter_map(|index| apparent_return_type.child(index))
12309            .any(|child| child.kind() == "::" && child.is_missing())
12310        && !normalize_cpp_whitespace(node_text(return_type, source)).is_empty()
12311        && !normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12312    {
12313        return Some((return_type, callable_name));
12314    }
12315    if !matches!(
12316        apparent_return_type.kind(),
12317        "identifier" | "field_identifier" | "type_identifier"
12318    ) || normalize_cpp_whitespace(node_text(apparent_return_type, source)).is_empty()
12319    {
12320        return None;
12321    }
12322    let parameters = function_declarator.child_by_field_name("parameters")?;
12323    let mut cursor = function_declarator.walk();
12324    let between = function_declarator
12325        .named_children(&mut cursor)
12326        .filter(|child| child.kind() != "comment")
12327        .filter(|child| {
12328            child.start_byte() >= apparent_return_type.end_byte()
12329                && child.end_byte() <= parameters.start_byte()
12330                && !same_node(*child, apparent_return_type)
12331                && !same_node(*child, parameters)
12332        })
12333        .collect::<Vec<_>>();
12334    let [name_error] = between.as_slice() else {
12335        return None;
12336    };
12337    if name_error.kind() != "ERROR" || name_error.named_child_count() != 1 {
12338        return None;
12339    }
12340    let callable_name = name_error.named_child(0)?;
12341    if !matches!(callable_name.kind(), "identifier" | "field_identifier")
12342        || normalize_cpp_whitespace(node_text(callable_name, source)).is_empty()
12343    {
12344        return None;
12345    }
12346    Some((apparent_return_type, callable_name))
12347}
12348
12349/// The part of a `function_declarator` after its parameter list that belongs to
12350/// the callable's identity: the cv-qualifiers, the ref-qualifier, the exception
12351/// specification, a trailing return type and a trailing requires-clause.
12352///
12353/// The grammar makes each of these a distinct sibling of the `parameters`
12354/// field, so they are read from the tree. Splitting the declarator's text on
12355/// the parameter list instead silently dropped every qualifier whenever the
12356/// parameter list was spelled with whitespace that normalization rewrote - a
12357/// line break or a double space was enough to make a `const` member definition
12358/// a different logical symbol from its declaration (#1827).
12359///
12360/// Attributes, `asm` blocks and the virtual specifiers (`override`, `final`)
12361/// are deliberately excluded. C++ does not make them part of the signature and
12362/// an out-of-line definition never repeats them, so including them would split
12363/// a declaration from its own definition.
12364fn cpp_declarator_identity_suffix(
12365    declarator: Node<'_>,
12366    parameters_node: Node<'_>,
12367    source: &str,
12368) -> String {
12369    let mut cursor = declarator.walk();
12370    let parts = declarator
12371        .named_children(&mut cursor)
12372        .filter(|child| child.start_byte() >= parameters_node.end_byte())
12373        .filter(|child| {
12374            matches!(
12375                child.kind(),
12376                "type_qualifier"
12377                    | "ref_qualifier"
12378                    | "noexcept"
12379                    | "throw_specifier"
12380                    | "trailing_return_type"
12381                    | "requires_clause"
12382            )
12383        })
12384        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
12385        .filter(|text| !text.is_empty())
12386        .collect::<Vec<_>>();
12387    normalize_cpp_qualifier_suffix(&parts.join(" "))
12388}
12389
12390/// The identity suffix of one callable declarator, for a consumer that holds
12391/// the declarator rather than the declaration walk's parts.
12392///
12393/// The persisted signature concatenates the parameter spelling and this suffix,
12394/// so a comparison that must agree on the suffix alone recomputes it here
12395/// instead of splitting the stored string.
12396pub(crate) fn cpp_callable_identity_suffix(
12397    function_declarator: Node<'_>,
12398    source: &str,
12399) -> Option<String> {
12400    let parameters_node = function_declarator.child_by_field_name("parameters")?;
12401    Some(cpp_declarator_identity_suffix(
12402        function_declarator,
12403        parameters_node,
12404        source,
12405    ))
12406}
12407
12408pub(crate) fn extract_function_declarator(node: Node<'_>) -> Option<Node<'_>> {
12409    match classify_declarator(node)? {
12410        DeclaratorKind::Function(function_declarator) => Some(function_declarator),
12411        DeclaratorKind::Variable(_) => None,
12412    }
12413}
12414
12415fn classify_declarator(node: Node<'_>) -> Option<DeclaratorKind<'_>> {
12416    match node.kind() {
12417        "function_declarator" => {
12418            let inner = node
12419                .child_by_field_name("declarator")
12420                .or_else(|| node.child_by_field_name("name"))
12421                .or_else(|| last_named_child(node));
12422            if inner.is_some_and(is_function_pointer_like_inner_declarator) {
12423                Some(DeclaratorKind::Variable(node))
12424            } else {
12425                Some(DeclaratorKind::Function(node))
12426            }
12427        }
12428        // A macro marker is declaration provenance, never a declarator: its
12429        // identifier names the macro, not anything the declaration declares.
12430        "macro_modifier" => None,
12431        "init_declarator"
12432        | "pointer_declarator"
12433        | "reference_declarator"
12434        | "parenthesized_declarator"
12435        | "array_declarator"
12436        | "attributed_declarator"
12437        | "template_function" => node
12438            .child_by_field_name("declarator")
12439            .or_else(|| node.child_by_field_name("name"))
12440            .or_else(|| last_named_child(node))
12441            .and_then(classify_declarator),
12442        "identifier" | "field_identifier" | "qualified_identifier" => {
12443            Some(DeclaratorKind::Variable(node))
12444        }
12445        _ => node
12446            .child_by_field_name("declarator")
12447            .or_else(|| node.child_by_field_name("name"))
12448            .or_else(|| last_named_child(node))
12449            .and_then(classify_declarator),
12450    }
12451}
12452
12453fn is_unfielded_declarator_candidate(node: Node<'_>) -> bool {
12454    matches!(
12455        node.kind(),
12456        "function_declarator"
12457            | "init_declarator"
12458            | "pointer_declarator"
12459            | "reference_declarator"
12460            | "parenthesized_declarator"
12461            | "array_declarator"
12462            | "attributed_declarator"
12463            | "template_function"
12464            | "macro_modifier_declarator"
12465            | "identifier"
12466            | "field_identifier"
12467            | "qualified_identifier"
12468    )
12469}
12470
12471fn has_direct_cpp_declarator(node: Node<'_>) -> bool {
12472    let class_like = first_class_like_child(node);
12473    let mut cursor = node.walk();
12474    node.named_children(&mut cursor).any(|child| {
12475        matches!(
12476            child.kind(),
12477            "init_declarator"
12478                | "pointer_declarator"
12479                | "reference_declarator"
12480                | "array_declarator"
12481                | "function_declarator"
12482                | "parenthesized_declarator"
12483                | "attributed_declarator"
12484        ) || matches!(
12485            child.kind(),
12486            "identifier" | "field_identifier" | "qualified_identifier"
12487        ) && class_like.is_none_or(|class_node| {
12488            child.start_byte() < class_node.start_byte() || child.end_byte() > class_node.end_byte()
12489        })
12490    })
12491}
12492
12493/// One namespace-scope forward class declaration that a recovered export-macro
12494/// class definition may borrow its identity from.  Tree-sitter can close a
12495/// malformed class at the enclosing namespace's closing brace, leaving the later
12496/// class definitions as root-level recovered `function_definition` nodes.  A
12497/// preceding `class Name;` in the same namespace is the only structured identity
12498/// signal available in that shape.
12499///
12500/// Everything recorded here is a property of the forward declaration alone.
12501/// What depends on the node doing the asking -- that the forward and its
12502/// namespace both close before it, with nothing but recovery trivia between --
12503/// stays in [`cpp_namespace_forward_matches_recovery`], so one fold over the
12504/// tree answers every later question about it.
12505struct CppNamespaceForward {
12506    name: String,
12507    start_byte: usize,
12508    /// Where the malformed namespace that held the forward ended.  No query
12509    /// asks anything else about that node.
12510    namespace_end_byte: usize,
12511    package_name: String,
12512}
12513
12514/// Read `node` as a borrowable namespace forward declaration.
12515///
12516/// The admission is deliberately conservative: only a body-less class specifier
12517/// whose declaration has no declarator, at namespace scope rather than inside a
12518/// function or class body, in a namespace that itself failed to parse.
12519fn cpp_namespace_forward_entry<'tree>(
12520    node: Node<'tree>,
12521    source: &str,
12522    ancestry: &ParentIndex<'tree>,
12523) -> Option<CppNamespaceForward> {
12524    if !matches!(
12525        node.kind(),
12526        "class_specifier" | "struct_specifier" | "union_specifier"
12527    ) || cpp_body_node(node).is_some()
12528    {
12529        return None;
12530    }
12531    let parent = node.parent()?;
12532    if !(parent.kind() == "declaration_list"
12533        || parent.kind() == "declaration" && !has_direct_cpp_declarator(parent))
12534    {
12535        return None;
12536    }
12537    let namespace = cpp_namespace_definition_for_forward(node, ancestry)?;
12538    // Borrowing is only justified by the parser-recovery shape we are
12539    // repairing: the namespace that held the forward must itself contain a
12540    // syntax error. A clean, unrelated namespace forward is not an identity
12541    // proof.
12542    if !namespace.has_error() {
12543        return None;
12544    }
12545    Some(CppNamespaceForward {
12546        name: class_like_name(node, source, ancestry)?,
12547        start_byte: node.start_byte(),
12548        namespace_end_byte: namespace.end_byte(),
12549        package_name: cpp_namespace_name_for_forward(node, source, ancestry)?,
12550    })
12551}
12552
12553/// Whether `forward` stands in the recovery relation to the node asking about
12554/// it: it and its malformed namespace both closed before the recovered class,
12555/// and nothing but recovery trivia separates the two.
12556fn cpp_namespace_forward_matches_recovery(
12557    forward: &CppNamespaceForward,
12558    recovered_node: Node<'_>,
12559) -> bool {
12560    forward.start_byte < recovered_node.start_byte()
12561        && forward.namespace_end_byte < recovered_node.start_byte()
12562        && malformed_namespace_is_nearest_recovery_region(
12563            forward.namespace_end_byte,
12564            recovered_node,
12565        )
12566}
12567
12568/// What one open [`CppVisitor::record_recovered_declarations`] has watched
12569/// happen to the declaration set.
12570///
12571/// The recovered set used to be a difference against a clone of the whole
12572/// declaration set, taken once per recovery: O(recoveries x declarations) on
12573/// exactly the error-recovered files that already walk slowest (#2787). The
12574/// walk knows which declarations it creates, so the capture collects them as
12575/// they are made and the difference is never needed.
12576///
12577/// `removed_pre_existing` is what makes that equal to the difference. A
12578/// deferred replacement removes the replaced declaration's children
12579/// (`ParsedFile::prepare_deferred_replacement`), and the reparse walk then
12580/// re-creates them. Creation alone cannot tell that apart from a first mint, so
12581/// a removal of a declaration this capture did not create records that it was
12582/// already there when the capture opened.
12583#[derive(Debug, Default)]
12584pub struct CppRecoveryCapture {
12585    /// Declarations created while this capture was open, in creation order.
12586    created: Vec<CodeUnit>,
12587    /// Membership for `created`.
12588    created_units: HashSet<CodeUnit>,
12589    /// Declarations that predate this capture and have been removed during it.
12590    removed_pre_existing: HashSet<CodeUnit>,
12591}
12592
12593/// Which tree a [`CppNamespaceForwardScan`] was folded out of.
12594///
12595/// A region reparse is its own tree and is dropped while the walk that made it
12596/// continues, so a later parse can be allocated at the same address and hand out
12597/// the same node ids.  The root's span and shape pin the identity its address
12598/// alone does not: two roots agreeing on all of this are the same parse of the
12599/// same bytes, and a scan of one is a scan of the other.
12600#[derive(PartialEq, Eq, Hash)]
12601pub struct CppTreeIdentity {
12602    root_id: usize,
12603    start_byte: usize,
12604    end_byte: usize,
12605    kind_id: u16,
12606    child_count: usize,
12607}
12608
12609impl CppTreeIdentity {
12610    fn of(root: Node<'_>) -> Self {
12611        Self {
12612            root_id: root.id(),
12613            start_byte: root.start_byte(),
12614            end_byte: root.end_byte(),
12615            kind_id: root.kind_id(),
12616            child_count: root.child_count(),
12617        }
12618    }
12619}
12620
12621/// The namespace forward declarations one tree's prefix holds, folded in as the
12622/// walk asks about them.
12623///
12624/// `scope_for_recovered_exported_class` asks the same question once per
12625/// recovered class, and the answer depends only on the part of the tree that
12626/// starts before the asking node.  Rescanning that prefix per question is
12627/// quadratic in the file, and a generated header whose parse recovery leaves
12628/// thousands of class-like declarations at file scope pays all of it: 1,904
12629/// questions over 1.13 billion node visits on one 7.25 MB Vulkan header
12630/// (#2754).  This carries the scan forward instead.  Each question advances the
12631/// traversal from wherever the last one stopped to the asking node's start byte,
12632/// so a whole walk pays at most one pass over the prefix its furthest question
12633/// reaches, and each question then costs a name lookup.
12634#[derive(Default)]
12635pub struct CppNamespaceForwardScan {
12636    /// Every named node starting before this byte has been folded in.
12637    scanned_through: usize,
12638    forwards: HashMap<String, Vec<CppNamespaceForward>>,
12639}
12640
12641impl CppNamespaceForwardScan {
12642    /// Fold in every named node of `root` that starts at or after the fold
12643    /// watermark and before `cutoff`.
12644    ///
12645    /// Preorder over a tree is nondecreasing in start byte, so the nodes this
12646    /// pass owes are exactly the ones no earlier pass reached, and a question
12647    /// about an earlier byte than one already answered costs nothing.
12648    fn advance_to<'tree>(
12649        &mut self,
12650        root: Node<'tree>,
12651        cutoff: usize,
12652        source: &str,
12653        ancestry: &ParentIndex<'tree>,
12654    ) {
12655        if cutoff <= self.scanned_through {
12656            return;
12657        }
12658        let folded_through = self.scanned_through;
12659        let mut cursor = root.walk();
12660        let mut stack = vec![root];
12661        while let Some(current) = stack.pop() {
12662            if (folded_through..cutoff).contains(&current.start_byte())
12663                && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12664            {
12665                self.forwards
12666                    .entry(forward.name.clone())
12667                    .or_default()
12668                    .push(forward);
12669            }
12670            // A subtree ending before the watermark holds only nodes an earlier
12671            // pass already folded, and one starting at or after the cutoff is
12672            // outside the prefix being asked about. Skipping both is what keeps
12673            // the total traversal to one pass.
12674            for child in current.named_children(&mut cursor) {
12675                if child.start_byte() < cutoff && child.end_byte() >= folded_through {
12676                    stack.push(child);
12677                }
12678            }
12679        }
12680        self.scanned_through = cutoff;
12681    }
12682
12683    /// The one namespace `name` was forward declared in before `recovered_node`.
12684    /// More than one matching forward declaration is ambiguous and answers
12685    /// nothing rather than guessing.
12686    fn unique_earlier_forward(&self, name: &str, recovered_node: Node<'_>) -> Option<String> {
12687        let mut matching = self
12688            .forwards
12689            .get(name)
12690            .into_iter()
12691            .flatten()
12692            .filter(|forward| cpp_namespace_forward_matches_recovery(forward, recovered_node));
12693        let first = matching.next()?;
12694        matching
12695            .next()
12696            .is_none()
12697            .then(|| first.package_name.clone())
12698    }
12699}
12700
12701/// The prefix scan [`CppNamespaceForwardScan`] replaces, kept as the oracle a
12702/// debug build checks every answer against (and the one the parity tests drive
12703/// directly).  It walks the whole prefix per question, which is exactly the cost
12704/// #2754 removed from the release path.
12705#[cfg(any(debug_assertions, test))]
12706fn unique_earlier_cpp_namespace_forward<'tree>(
12707    recovered_node: Node<'tree>,
12708    name: &str,
12709    source: &str,
12710    ancestry: &ParentIndex<'tree>,
12711) -> Option<String> {
12712    let mut root = recovered_node;
12713    while let Some(parent) = ancestry.parent(root) {
12714        root = parent;
12715    }
12716
12717    let mut candidates = Vec::new();
12718    let mut stack = vec![root];
12719    while let Some(current) = stack.pop() {
12720        if current.start_byte() < recovered_node.start_byte()
12721            && let Some(forward) = cpp_namespace_forward_entry(current, source, ancestry)
12722            && forward.name == name
12723            && cpp_namespace_forward_matches_recovery(&forward, recovered_node)
12724        {
12725            candidates.push(forward.package_name);
12726        }
12727
12728        let mut cursor = current.walk();
12729        for child in current.named_children(&mut cursor) {
12730            if child.start_byte() < recovered_node.start_byte() {
12731                stack.push(child);
12732            }
12733        }
12734    }
12735
12736    if candidates.len() == 1 {
12737        candidates.pop()
12738    } else {
12739        None
12740    }
12741}
12742
12743fn malformed_namespace_is_nearest_recovery_region(
12744    namespace_end_byte: usize,
12745    recovered_node: Node<'_>,
12746) -> bool {
12747    let mut root = recovered_node;
12748    while let Some(parent) = root.parent() {
12749        root = parent;
12750    }
12751    let mut cursor = root.walk();
12752    root.named_children(&mut cursor)
12753        .filter(|sibling| {
12754            namespace_end_byte <= sibling.start_byte()
12755                && sibling.end_byte() <= recovered_node.start_byte()
12756        })
12757        .all(is_malformed_namespace_recovery_trivia)
12758}
12759
12760fn is_malformed_namespace_recovery_trivia(node: Node<'_>) -> bool {
12761    matches!(node.kind(), "ERROR" | "comment")
12762        || node.kind().starts_with("preproc_")
12763        || node.kind() == "expression_statement" && node.named_child_count() == 0
12764}
12765
12766/// Return the namespace path for a forward class only when the declaration is
12767/// at namespace scope.  A declaration nested in a function/class body may share
12768/// the same namespace ancestor but cannot identify a top-level class definition.
12769fn cpp_namespace_name_for_forward<'tree>(
12770    node: Node<'tree>,
12771    source: &str,
12772    ancestry: &ParentIndex<'tree>,
12773) -> Option<String> {
12774    cpp_namespace_definition_for_forward(node, ancestry)?;
12775    cpp_lexical_namespace_name(node, source)
12776}
12777
12778fn cpp_namespace_definition_for_forward<'tree>(
12779    node: Node<'tree>,
12780    ancestry: &ParentIndex<'tree>,
12781) -> Option<Node<'tree>> {
12782    let declaration = ancestry.parent(node)?;
12783    let mut ancestor = ancestry.parent(declaration);
12784    while let Some(current) = ancestor {
12785        if matches!(
12786            current.kind(),
12787            "compound_statement"
12788                | "field_declaration_list"
12789                | "class_specifier"
12790                | "struct_specifier"
12791                | "union_specifier"
12792                | "function_definition"
12793                | "lambda_expression"
12794        ) {
12795            return None;
12796        }
12797        if current.kind() == "namespace_definition" {
12798            return Some(current);
12799        }
12800        ancestor = ancestry.parent(current);
12801    }
12802    None
12803}
12804
12805fn is_function_pointer_like_inner_declarator(node: Node<'_>) -> bool {
12806    match node.kind() {
12807        "template_function" => node
12808            .child_by_field_name("name")
12809            .is_some_and(is_function_pointer_like_inner_declarator),
12810        _ => is_pointer_wrapper_declarator(node),
12811    }
12812}
12813
12814fn is_pointer_wrapper_declarator(node: Node<'_>) -> bool {
12815    match node.kind() {
12816        "pointer_declarator" | "reference_declarator" | "array_declarator" => true,
12817        "parenthesized_declarator" => node
12818            .child_by_field_name("declarator")
12819            .or_else(|| last_named_child(node))
12820            .is_some_and(is_pointer_wrapper_declarator),
12821        _ => false,
12822    }
12823}
12824
12825/// The canonical owner chain and package of an out-of-line member whose
12826/// written qualifier starts with a name a `using ns::Type;` declaration bound
12827/// in the definition's own namespace scope (#6902 item 4).
12828///
12829/// `using imported::Type; void Type::method() {}` defines
12830/// `imported::Type::method`: the written `Type::` stays in the source, but the
12831/// member's identity follows the binding. The binding proves the owner only
12832/// when every visible binding of that name in this scope names the same
12833/// target. Conflicting bindings, a binding from an enclosing namespace (which
12834/// a same-named class in the inner namespace would shadow), and a using
12835/// declaration this walk cannot see (a preprocessor arm, another file) all
12836/// return `None`, and the caller keeps the written-qualifier reading.
12837fn using_bound_cpp_owner(
12838    declarator_name: Node<'_>,
12839    source: &str,
12840    scope: &ScopeInfo,
12841) -> Option<(Vec<String>, String)> {
12842    if scope.class_unit.is_some() || scope.visible_using_declarations.is_empty() {
12843        return None;
12844    }
12845    let (components, explicitly_global) =
12846        structured_cpp_qualified_components(declarator_name, source)?;
12847    let (_, owner) = components.split_last()?;
12848    if explicitly_global
12849        || owner.is_empty()
12850        || components.iter().any(|component| component.is_template_id)
12851    {
12852        return None;
12853    }
12854    let mut targets = scope
12855        .visible_using_declarations
12856        .iter()
12857        .filter(|binding| {
12858            binding.scope_package == scope.package_name && binding.name == owner[0].name
12859        })
12860        .map(|binding| binding.target_package.as_str());
12861    let target_package = targets.next()?;
12862    if targets.any(|other| other != target_package) {
12863        return None;
12864    }
12865    Some((
12866        owner
12867            .iter()
12868            .map(|component| component.name.clone())
12869            .collect(),
12870        target_package.to_string(),
12871    ))
12872}
12873
12874fn split_cpp_name(raw_name: &str, scope: &ScopeInfo) -> (Option<CppMemberOwner>, String, String) {
12875    let cleaned = raw_name.trim_start_matches("template ").trim();
12876    // A leading `::` is the explicit-global marker, not an empty owner segment.
12877    // Error recovery can leave a definition spelled `::X(...)` (e.g. an
12878    // erroneous macro envelope swallowing the first identifier of an
12879    // out-of-line `X::X` constructor, chromium #1573); without this strip the
12880    // split below yields owner_parts `[""]`, constructing a unit with an empty
12881    // owner chain (`short ".X"`) that the FqName boundary assert rejects.
12882    let cleaned = cleaned.trim_start_matches("::");
12883    // Parser recovery can preserve two adjacent scope operators around a
12884    // missing component (for example `X::/**/::method` in compiler diagnostic
12885    // fixtures). Empty components are syntax-recovery artifacts, never C++
12886    // owners. Keeping one as the final owner constructed `short_name=".method"`
12887    // and violated the structured package/short boundary during a large LLVM
12888    // workspace build. This is the same legacy-string-to-FqName bridge as the
12889    // ordinary split above; discard only components that the delimiter itself
12890    // proves empty.
12891    let parts: Vec<_> = cleaned
12892        .split("::")
12893        .filter(|component| !component.is_empty())
12894        .collect();
12895    if parts.is_empty() {
12896        return (None, cleaned.to_string(), scope.package_name.clone());
12897    }
12898    if parts.len() > 1 {
12899        let name = parts.last().unwrap_or(&cleaned).to_string();
12900        let owner_parts = &parts[..parts.len() - 1];
12901        if let Some(class_unit) = &scope.class_unit {
12902            // Lexically inside a class body: the owner is that class, whatever
12903            // the declarator re-qualifies it as.
12904            return (
12905                Some(CppMemberOwner::Unit(class_unit.clone())),
12906                name,
12907                scope.package_name.clone(),
12908            );
12909        }
12910        if !scope.package_name.is_empty() {
12911            // Out-of-line member definition written *inside* an enclosing
12912            // `namespace {}` block (scope package is that namespace). Every
12913            // owner segment before the terminal member is a class-nesting step
12914            // -- an out-of-line nested-class member `Outer::Inner::method` in
12915            // Bifrost's `Outer$Inner` short-name convention (#1121) -- not a
12916            // namespace path: `using namespace` never brings nested-class
12917            // access into unqualified scope, so C++ always writes the full
12918            // `Outer::Inner::` qualifier here. The only wrinkle is a definition
12919            // that redundantly re-states the enclosing namespace it already
12920            // sits in (`namespace log4cxx { void log4cxx::Foo::method() {} }`);
12921            // strip that re-qualifying prefix (which duplicates a suffix of the
12922            // enclosing package path) before treating what remains as the
12923            // nested-class chain, so the redundant spelling still lands on the
12924            // same `log4cxx.Foo.method` identity as its header declaration.
12925            let nested = strip_redundant_namespace_prefix(owner_parts, &scope.package_name);
12926            let owner = (!nested.is_empty()).then(|| {
12927                CppMemberOwner::Chain(nested.iter().map(|name| name.to_string()).collect())
12928            });
12929            return (owner, name, scope.package_name.clone());
12930        }
12931        // File scope (no enclosing `namespace {}` block, scope package empty).
12932        let (owner, package_name) = if owner_parts.len() > 1 {
12933            // A multi-segment qualifier at file scope with no enclosing
12934            // namespace: treat all but the last owner segment as the namespace
12935            // path and the last as the owning class (`ns1::ns2::Class::method`
12936            // -> package `ns1::ns2`, owner `Class`). Whether a leading segment
12937            // is really a namespace or an outer class cannot be told from the
12938            // declarator text alone here, and no enclosing namespace or
12939            // in-index owner is available at per-file extraction to confirm the
12940            // class reading, so the far-more-common namespace interpretation is
12941            // kept rather than guessed away (the nested-class-at-file-scope and
12942            // using-directive-qualified nested-class shapes remain on this
12943            // behavior; see #1121).
12944            (
12945                Some(CppMemberOwner::Chain(vec![
12946                    owner_parts.last().unwrap_or(&"").to_string(),
12947                ])),
12948                owner_parts[..owner_parts.len() - 1].join("::"),
12949            )
12950        } else {
12951            // A bare `Class::member` qualifier at file scope carries no
12952            // namespace segment of its own. The declarator alone cannot say
12953            // which namespace owns `Class` -- but a `using namespace X;`
12954            // directive already in effect at this point in the file (#1093,
12955            // e.g. log4cxx's `using namespace LOG4CXX_NS;` followed by
12956            // out-of-line `LogString HTMLLayout::getContentType() const {...}`)
12957            // is the remaining structural signal for it, so fall back to it
12958            // rather than leaving the definition's package empty while its
12959            // header declaration (parsed inside the `namespace {}` block) keeps
12960            // the real one -- an identity split that made the same member
12961            // unresolvable under its own displayed spelling.
12962            (
12963                Some(CppMemberOwner::Chain(vec![owner_parts[0].to_string()])),
12964                cpp_using_directive_namespace_for_bare_owner(scope),
12965            )
12966        };
12967        return (owner, name, package_name);
12968    }
12969
12970    let package_name = scope.package_name.clone();
12971    let owner = scope
12972        .class_unit
12973        .as_ref()
12974        .map(|parent| CppMemberOwner::Unit(parent.clone()));
12975    (owner, cleaned.to_string(), package_name)
12976}
12977
12978/// Drop the leading owner segments of an out-of-line member qualifier that
12979/// merely re-state the enclosing namespace the definition already sits in, so
12980/// what remains is the pure class-nesting chain. Inside `namespace a::b`, a
12981/// definition may redundantly write `a::b::Outer::Inner::method` (or the
12982/// partial `b::Outer::Inner::method`); the leading segments that duplicate a
12983/// suffix of the enclosing package path (`a::b`, then `b`) are re-qualification
12984/// noise, not class-nesting steps. Returns the owner segments with the longest
12985/// such re-qualifying prefix removed (possibly all of them, when the qualifier
12986/// names only the enclosing namespace before the terminal member -- a
12987/// re-qualified free function). `package_name` is the enclosing namespace path
12988/// in its stored `::`-joined form; both sides are split on the same delimiter
12989/// the namespace walker joined them with, so this compares namespace *segments*
12990/// rather than scanning text.
12991fn strip_redundant_namespace_prefix<'a>(
12992    owner_parts: &'a [&'a str],
12993    package_name: &str,
12994) -> &'a [&'a str] {
12995    if package_name.is_empty() {
12996        return owner_parts;
12997    }
12998    let package_segments: Vec<&str> = package_name.split("::").collect();
12999    let max_prefix = owner_parts.len().min(package_segments.len());
13000    for prefix_len in (1..=max_prefix).rev() {
13001        let package_suffix = &package_segments[package_segments.len() - prefix_len..];
13002        if &owner_parts[..prefix_len] == package_suffix {
13003            return &owner_parts[prefix_len..];
13004        }
13005    }
13006    owner_parts
13007}
13008
13009/// Best-effort package-name recovery for a bare (unqualified-by-itself) owner
13010/// class name at file/namespace scope, from the `using namespace` directives
13011/// visible at this point in the file. Several may be in scope at once (a
13012/// primary `using namespace NS;` alongside deeper conveniences like `using
13013/// namespace NS::helpers;`); since the declarator gives no way to tell which
13014/// one actually declares the owner class, prefer the shallowest (fewest
13015/// `::`-separated segments) as the file's most likely "home" namespace,
13016/// breaking ties by declaration order. Returns an empty string (leaving the
13017/// caller's package unqualified, as before) when no using-namespace directive
13018/// is in scope.
13019fn cpp_using_directive_namespace_for_bare_owner(scope: &ScopeInfo) -> String {
13020    scope
13021        .visible_using_namespaces
13022        .iter()
13023        .min_by_key(|namespace| namespace.split("::").count())
13024        .cloned()
13025        .unwrap_or_default()
13026}
13027
13028struct CppQualifiedNameComponent {
13029    name: String,
13030    is_template_id: bool,
13031}
13032
13033/// Canonical nested-class chain for an out-of-line class definition written
13034/// inside its namespace, such as `struct Outer::Inner { ... }`, as one
13035/// component per class (`["Outer", "Inner"]`).
13036///
13037/// The enclosing namespace fixes the namespace/class boundary: after an
13038/// optional redundant spelling of that namespace, every component belongs to
13039/// the class chain. File-scope qualified class names remain untouched because
13040/// syntax alone cannot distinguish `namespace::Class` from `Outer::Inner`.
13041///
13042/// The components stay structured (rather than being `$`-joined here) so the
13043/// fq construction can push one Type/Nested segment per class; the `$`-joined
13044/// short-name display form is derived at the call sites that need it.
13045fn qualified_class_name_chain(
13046    class_node: Node<'_>,
13047    source: &str,
13048    scope: &ScopeInfo,
13049) -> Option<Vec<String>> {
13050    if scope.package_name.is_empty() || scope.class_unit.is_some() {
13051        return None;
13052    }
13053    let name = class_node.child_by_field_name("name")?;
13054    let (components, explicitly_global) = structured_cpp_qualified_components(name, source)?;
13055    if explicitly_global
13056        || components.len() < 2
13057        || components.iter().any(|component| component.is_template_id)
13058    {
13059        return None;
13060    }
13061    let names = components
13062        .iter()
13063        .map(|component| component.name.as_str())
13064        .collect::<Vec<_>>();
13065    let class_chain = strip_redundant_namespace_prefix(&names, &scope.package_name);
13066    if class_chain.is_empty() {
13067        return None;
13068    }
13069    Some(class_chain.iter().map(|name| name.to_string()).collect())
13070}
13071
13072fn structured_cpp_qualified_components(
13073    qualified_name: Node<'_>,
13074    source: &str,
13075) -> Option<(Vec<CppQualifiedNameComponent>, bool)> {
13076    if qualified_name.kind() != "qualified_identifier" {
13077        return None;
13078    }
13079
13080    let mut components = Vec::new();
13081    let mut current = qualified_name;
13082    let mut explicitly_global = false;
13083    loop {
13084        if current.kind() == "qualified_identifier" {
13085            if let Some(component) = current.child_by_field_name("scope") {
13086                components.push(canonical_cpp_qualified_component(component, source)?);
13087            } else if components.is_empty() {
13088                explicitly_global = true;
13089            } else {
13090                return None;
13091            }
13092            current = current.child_by_field_name("name")?;
13093        } else {
13094            components.push(canonical_cpp_qualified_component(current, source)?);
13095            break;
13096        }
13097    }
13098    Some((components, explicitly_global))
13099}
13100
13101fn split_structured_templated_cpp_name(
13102    declarator: Node<'_>,
13103    declarator_name: Node<'_>,
13104    source: &str,
13105    scope: &ScopeInfo,
13106) -> Option<(Option<CppMemberOwner>, String, String)> {
13107    let (mut components, explicitly_global) =
13108        structured_cpp_qualified_components(declarator_name, source)?;
13109
13110    let terminal = components.pop()?;
13111    let owner_start = components
13112        .iter()
13113        .position(|component| component.is_template_id)?;
13114    let explicit_package = components[..owner_start]
13115        .iter()
13116        .map(|component| component.name.as_str())
13117        .collect::<Vec<_>>()
13118        .join("::");
13119    let explicit_package_is_empty = explicit_package.is_empty();
13120    let package_name = match (
13121        explicitly_global,
13122        scope.package_name.is_empty(),
13123        explicit_package_is_empty,
13124    ) {
13125        (true, _, _) => explicit_package,
13126        (false, _, true) => scope.package_name.clone(),
13127        (false, true, false) => explicit_package,
13128        (false, false, false) => format!("{}::{explicit_package}", scope.package_name),
13129    };
13130    // Same identity-split fallback as `split_cpp_name` (#1093): a template
13131    // specialization's owner class named with no namespace segment of its own
13132    // (`explicit_package` empty) at file scope (`explicitly_global` false)
13133    // with nothing enclosing (`package_name` still empty) has no structural
13134    // signal for its namespace besides an in-scope `using namespace X;`.
13135    let package_name = if package_name.is_empty() && !explicitly_global && explicit_package_is_empty
13136    {
13137        cpp_using_directive_namespace_for_bare_owner(scope)
13138    } else {
13139        package_name
13140    };
13141    // A specialization member keeps its specialization owner (`Box<T*>`)
13142    // rather than collapsing into the primary's; a qualifier the template
13143    // levels cannot read keeps the bare names.
13144    let owner_chain = match cpp_out_of_line_owner_names(declarator, source)
13145        .filter(|names| names.len() == components.len())
13146    {
13147        Some(mut names) => names.split_off(owner_start),
13148        None => components[owner_start..]
13149            .iter()
13150            .map(|component| component.name.clone())
13151            .collect::<Vec<_>>(),
13152    };
13153    if owner_chain.is_empty() || terminal.name.is_empty() {
13154        return None;
13155    }
13156
13157    Some((
13158        Some(CppMemberOwner::Chain(owner_chain)),
13159        terminal.name,
13160        package_name,
13161    ))
13162}
13163
13164fn canonical_cpp_qualified_component(
13165    mut component: Node<'_>,
13166    source: &str,
13167) -> Option<CppQualifiedNameComponent> {
13168    let mut is_template_id = false;
13169    loop {
13170        match component.kind() {
13171            "template_type" => {
13172                is_template_id = true;
13173                component = component.child_by_field_name("name")?;
13174            }
13175            "dependent_name" => component = component.named_child(0)?,
13176            "identifier"
13177            | "field_identifier"
13178            | "namespace_identifier"
13179            | "type_identifier"
13180            | "operator_name"
13181            | "destructor_name" => {
13182                let name = normalize_cpp_whitespace(node_text(component, source));
13183                return (!name.is_empty()).then_some(CppQualifiedNameComponent {
13184                    name,
13185                    is_template_id,
13186                });
13187            }
13188            _ => component = component.child_by_field_name("name")?,
13189        }
13190    }
13191}
13192
13193fn extract_declarator_name(node: Node<'_>, source: &str) -> String {
13194    if let Some(name) = macro_decorated_unqualified_name(node) {
13195        return extract_declarator_name(name, source);
13196    }
13197    match node.kind() {
13198        "identifier"
13199        | "field_identifier"
13200        | "type_identifier"
13201        | "operator_name"
13202        | "destructor_name"
13203        | "qualified_identifier" => node_text(node, source).to_string(),
13204        "function_declarator"
13205        | "pointer_declarator"
13206        | "reference_declarator"
13207        | "parenthesized_declarator"
13208        | "array_declarator"
13209        | "template_function" => node
13210            .child_by_field_name("declarator")
13211            .or_else(|| node.child_by_field_name("name"))
13212            .or_else(|| last_named_child(node))
13213            .map(|child| extract_declarator_name(child, source))
13214            .unwrap_or_else(|| node_text(node, source).to_string()),
13215        _ => node
13216            .child_by_field_name("name")
13217            .map(|child| extract_declarator_name(child, source))
13218            .unwrap_or_else(|| node_text(node, source).to_string()),
13219    }
13220}
13221
13222/// Extract a callable identity only through declaration-shaped AST nodes.
13223/// Error recovery around trailing `decltype((object.*f)(...))` expressions can
13224/// expose the call's parameter list as a false function declarator; accepting
13225/// arbitrary node text there emitted bogus names such as `.*f`.
13226fn extract_callable_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13227    if let Some(name) = macro_decorated_unqualified_name(node) {
13228        return extract_callable_declarator_name(name, source);
13229    }
13230    match node.kind() {
13231        "identifier"
13232        | "field_identifier"
13233        | "type_identifier"
13234        | "operator_name"
13235        | "destructor_name"
13236        | "qualified_identifier" => Some(node_text(node, source).to_string()),
13237        "function_declarator"
13238        | "pointer_declarator"
13239        | "reference_declarator"
13240        | "parenthesized_declarator"
13241        | "array_declarator"
13242        | "template_function" => node
13243            .child_by_field_name("declarator")
13244            .or_else(|| node.child_by_field_name("name"))
13245            .and_then(|child| extract_callable_declarator_name(child, source)),
13246        _ => None,
13247    }
13248}
13249
13250fn extract_variable_name(mut node: Node<'_>, source: &str) -> Option<String> {
13251    loop {
13252        if let Some(name) = macro_decorated_unqualified_name(node) {
13253            node = name;
13254            continue;
13255        }
13256        match node.kind() {
13257            "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
13258                let name = node_text(node, source).trim().to_string();
13259                return (!name.is_empty()).then_some(name);
13260            }
13261            _ => {
13262                node = node
13263                    .child_by_field_name("declarator")
13264                    .or_else(|| node.child_by_field_name("name"))
13265                    .or_else(|| last_named_child(node))?;
13266            }
13267        }
13268    }
13269}
13270
13271/// Recover a C field whose name is wrapped in the function-like
13272/// `MBEDTLS_PRIVATE(name)` macro. Tree-sitter represents this invocation as a
13273/// function declarator, while retaining its argument as a structured child.
13274/// The exact macro name and one-argument shape keep ordinary function-pointer
13275/// fields and unknown malformed declarators fail-closed.
13276#[derive(Clone, Copy)]
13277pub(crate) struct RecoveredFunctionLikeFieldDeclarator<'tree> {
13278    pub(crate) name: Node<'tree>,
13279    pub(crate) declarator: Node<'tree>,
13280}
13281
13282impl RecoveredFunctionLikeFieldDeclarator<'_> {
13283    pub(crate) fn pointer_depth(self) -> i32 {
13284        let mut depth = 0;
13285        let mut current = self.declarator;
13286        while current.kind() != "function_declarator" {
13287            if current.kind() == "pointer_declarator" {
13288                depth += 1;
13289            }
13290            current = current
13291                .child_by_field_name("declarator")
13292                .expect("recovered field wrapper has an inner declarator");
13293        }
13294        depth
13295    }
13296}
13297
13298/// Whether a class-scope field declaration is really a function-like macro
13299/// invocation whose replacement the parser never saw.
13300///
13301/// The invocation carries no terminator of its own, so tree-sitter invents a
13302/// zero-width `;` and reads the macro name as the member's type and the
13303/// argument list as a `parenthesized_declarator`. That shape is not a member:
13304/// a member terminates itself, and with a real terminator the grammar reads
13305/// `T (name);` as a declaration rather than a field. Only an identifier-shaped
13306/// type can name the macro being invoked; a builtin type keeps whatever
13307/// recovery the ordinary declarator walk makes of it.
13308fn cpp_unterminated_macro_invocation_field(node: Node<'_>) -> bool {
13309    if node.kind() != "field_declaration" {
13310        return false;
13311    }
13312    let Some(type_node) = node.child_by_field_name("type") else {
13313        return false;
13314    };
13315    if !matches!(type_node.kind(), "identifier" | "type_identifier") {
13316        return false;
13317    }
13318    let Some(declarator) = node.child_by_field_name("declarator") else {
13319        return false;
13320    };
13321    if declarator.kind() != "parenthesized_declarator" {
13322        return false;
13323    }
13324    let mut invented_terminator = false;
13325    for child in children_iter(node) {
13326        if child.kind() != ";" {
13327            continue;
13328        }
13329        if !child.is_missing() {
13330            return false;
13331        }
13332        invented_terminator = true;
13333    }
13334    invented_terminator
13335}
13336
13337/// An access label that a class-scope function-like macro invocation
13338/// swallowed, as [`cpp_macro_swallowed_access_label`] reads it.
13339pub(crate) struct CppSwallowedAccessLabel<'tree> {
13340    /// The access keyword, which the grammar read as a `field_identifier`.
13341    pub(crate) keyword: Node<'tree>,
13342    /// `"public"`, `"protected"` or `"private"`.
13343    pub(crate) access: &'static str,
13344}
13345
13346/// Whether a class-body member is a function-like macro invocation that
13347/// swallowed the access label written after it, and with it the members under
13348/// that label.
13349///
13350/// `DISABLE_COPY_ASSIGN_MOVE(ClosedDetect)` carries no terminator of its own
13351/// (#3298). The macro-declaration grammar (brokk-tree-sitter-cpp 0.24.4) reads
13352/// the invocation as a `macro_type_specifier`, the `public` after it as the
13353/// member's `field_identifier` declarator, and the label's `:` as a bitfield
13354/// whose width expression shreds the next member, so the constructor written
13355/// under the label is left with no declarator at all. An access keyword is
13356/// never a declarator name, and a bitfield after it is how the parser spent
13357/// the label's colon, so the shape is the evidence. The access ladders read
13358/// the label from it too, because it still applies to every member after it.
13359pub(crate) fn cpp_macro_swallowed_access_label<'tree>(
13360    node: Node<'tree>,
13361    source: &str,
13362) -> Option<CppSwallowedAccessLabel<'tree>> {
13363    if !matches!(node.kind(), "field_declaration" | "function_definition") {
13364        return None;
13365    }
13366    node.child_by_field_name("type")
13367        .filter(|type_node| type_node.kind() == "macro_type_specifier")?;
13368    let keyword = node
13369        .child_by_field_name("declarator")
13370        .filter(|declarator| declarator.kind() == "field_identifier")?;
13371    let access = match node_text(keyword, source) {
13372        "public" => "public",
13373        "protected" => "protected",
13374        "private" => "private",
13375        _ => return None,
13376    };
13377    let mut clause = keyword.next_sibling()?;
13378    if clause.kind() == "ERROR" {
13379        clause = clause.child(0)?;
13380    }
13381    if clause.kind() != "bitfield_clause" || clause.child(0)?.kind() != ":" {
13382        return None;
13383    }
13384    Some(CppSwallowedAccessLabel { keyword, access })
13385}
13386
13387pub(crate) fn recovered_function_like_field_declarator<'tree>(
13388    node: Node<'tree>,
13389    source: &str,
13390) -> Option<RecoveredFunctionLikeFieldDeclarator<'tree>> {
13391    if node.kind() != "field_declaration" {
13392        return None;
13393    }
13394    let outer_declarator = node.child_by_field_name("declarator")?;
13395    let mut declarator = outer_declarator;
13396    while matches!(
13397        declarator.kind(),
13398        "pointer_declarator"
13399            | "reference_declarator"
13400            | "array_declarator"
13401            | "parenthesized_declarator"
13402    ) {
13403        declarator = declarator.child_by_field_name("declarator")?;
13404    }
13405    if declarator.kind() != "function_declarator" {
13406        return None;
13407    }
13408    let macro_name = declarator.child_by_field_name("declarator")?;
13409    if macro_name.kind() != "field_identifier" || node_text(macro_name, source) != "MBEDTLS_PRIVATE"
13410    {
13411        return None;
13412    }
13413    let parameters = declarator.child_by_field_name("parameters")?;
13414    let mut cursor = parameters.walk();
13415    let mut arguments = parameters.named_children(&mut cursor);
13416    let parameter = arguments.next()?;
13417    if arguments.next().is_some() || parameter.kind() != "parameter_declaration" {
13418        return None;
13419    }
13420    let name = parameter.child_by_field_name("type").filter(|argument| {
13421        matches!(
13422            argument.kind(),
13423            "identifier" | "field_identifier" | "type_identifier"
13424        )
13425    })?;
13426    Some(RecoveredFunctionLikeFieldDeclarator {
13427        name,
13428        declarator: outer_declarator,
13429    })
13430}
13431
13432use brokk_bifrost_core::analyzer::tree_walk::last_named_child;
13433
13434fn extract_alias_declaration_name(node: Node<'_>, source: &str) -> Option<String> {
13435    let name_node = node.child_by_field_name("name")?;
13436    let name = normalize_cpp_whitespace(node_text(name_node, source));
13437    (!name.is_empty()).then_some(name)
13438}
13439
13440fn recovered_type_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13441    if node.kind() != "declaration" {
13442        return Vec::new();
13443    }
13444    // The `typedef`/`using` keyword: a `type_identifier` in the type field,
13445    // or -- under the macro-declaration grammar, which reads the keyword as
13446    // an object-like macro -- the identifier a leading `macro_modifier`
13447    // wraps, with the aliased type in the type field.
13448    let keyword_is_alias = node
13449        .child_by_field_name("type")
13450        .filter(|node| {
13451            node.kind() == "type_identifier"
13452                && matches!(node_text(*node, source), "using" | "typedef")
13453        })
13454        .map(|keyword| node_text(keyword, source).to_owned())
13455        .or_else(|| {
13456            node.named_children(&mut node.walk())
13457                .find_map(macro_modifier_identifier)
13458                .map(|identifier| node_text(identifier, source).to_owned())
13459                .filter(|text| matches!(text.as_str(), "using" | "typedef"))
13460        });
13461    let Some(keyword_is_alias) = keyword_is_alias else {
13462        return Vec::new();
13463    };
13464    let Some(declarator) = node.child_by_field_name("declarator") else {
13465        return Vec::new();
13466    };
13467    if keyword_is_alias == "using"
13468        && (declarator.kind() != "init_declarator"
13469            || declarator.child_by_field_name("value").is_none())
13470    {
13471        return Vec::new();
13472    }
13473    if keyword_is_alias == "typedef"
13474        && let Some(alias_name) = recovered_typedef_error_alias_name(node, declarator, source)
13475    {
13476        return vec![alias_name];
13477    }
13478    extract_typedef_declarator_name(declarator, source)
13479        .into_iter()
13480        .collect()
13481}
13482
13483/// The RHS retained by the same malformed alias shapes declaration admission
13484/// accepts. The alias name proves which declarator owns this source evidence.
13485pub(crate) fn recovered_alias_type_node<'tree>(
13486    node: Node<'tree>,
13487    alias_name: &str,
13488    source: &str,
13489) -> Option<Node<'tree>> {
13490    if node.kind() != "declaration" {
13491        return None;
13492    }
13493    let declarator = node.child_by_field_name("declarator")?;
13494    let admitted = recovered_type_alias_names(node, source)
13495        .iter()
13496        .any(|name| name == alias_name)
13497        || recovered_using_declaration_alias_name(node, source).as_deref() == Some(alias_name);
13498    if !admitted {
13499        return None;
13500    }
13501    if declarator.kind() == "init_declarator" {
13502        return declarator.child_by_field_name("value");
13503    }
13504    // The macro-declaration grammar keeps the aliased type in the type field
13505    // and the keyword as a `macro_modifier`; the alias name is the
13506    // declarator, so the type field is the RHS evidence.
13507    if node
13508        .named_children(&mut node.walk())
13509        .find_map(macro_modifier_identifier)
13510        .is_some_and(|keyword| matches!(node_text(keyword, source), "using" | "typedef"))
13511    {
13512        return node.child_by_field_name("type");
13513    }
13514    (recovered_typedef_error_alias_name(node, declarator, source).as_deref() == Some(alias_name))
13515        .then_some(declarator)
13516}
13517
13518fn recovered_typedef_error_alias_name(
13519    declaration: Node<'_>,
13520    declarator: Node<'_>,
13521    source: &str,
13522) -> Option<String> {
13523    // An export macro between `class` and its name can make tree-sitter parse
13524    // the recovered class body as a function body. In that shape,
13525    //
13526    //     typedef spi::Filter BASE_CLASS;
13527    //
13528    // becomes a declaration whose `declarator` is the underlying qualified
13529    // type (`spi::Filter`) and whose actual alias name is displaced into the
13530    // following ERROR node. Do not publish the terminal underlying type
13531    // (`Filter`) as a false class-owned alias.
13532    if declarator.kind() != "qualified_identifier" {
13533        return None;
13534    }
13535    let mut cursor = declaration.walk();
13536    let mut errors = declaration
13537        .named_children(&mut cursor)
13538        .filter(|child| child.kind() == "ERROR" && child.start_byte() >= declarator.end_byte());
13539    let error = errors.next()?;
13540    if errors.next().is_some() || error.named_child_count() != 1 {
13541        return None;
13542    }
13543    let name = error.named_child(0)?;
13544    if !matches!(
13545        name.kind(),
13546        "identifier" | "field_identifier" | "type_identifier"
13547    ) {
13548        return None;
13549    }
13550    let name = normalize_cpp_whitespace(node_text(name, source));
13551    (!name.is_empty()).then_some(name)
13552}
13553
13554fn extract_typedef_alias_names(node: Node<'_>, source: &str) -> Vec<String> {
13555    // A function-like token in the type position can make tree-sitter expose
13556    // its argument as a parenthesized declarator. Do not publish that argument
13557    // as an alias. The macro-specific recovery below handles the proven shape.
13558    if fragmented_parenthesized_typedef_type(node).is_some() {
13559        return Vec::new();
13560    }
13561    let has_function_like_macro_type = node
13562        .child_by_field_name("type")
13563        .filter(|type_node| type_node.kind() == "type_identifier")
13564        .is_some_and(|type_node| {
13565            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13566        });
13567    let mut names = Vec::new();
13568    let mut cursor = node.walk();
13569    for declarator in node.children_by_field_name("declarator", &mut cursor) {
13570        if has_function_like_macro_type && declarator.kind() == "parenthesized_declarator" {
13571            continue;
13572        }
13573        if let Some(name) = extract_typedef_declarator_name(declarator, source)
13574            && !names.contains(&name)
13575        {
13576            names.push(name);
13577        }
13578    }
13579    names
13580}
13581
13582struct RecoveredMacroTypedefAlias<'tree> {
13583    name: String,
13584    end_node: Node<'tree>,
13585}
13586
13587/// Recover `typedef MACRO(type) alias;` when tree-sitter splits the final alias
13588/// into an identifier expression statement. The uppercase macro token, missing
13589/// typedef terminator, and complete sibling terminator prove this exact shape.
13590fn recovered_macro_typedef_alias<'tree>(
13591    node: Node<'tree>,
13592    source: &str,
13593) -> Option<RecoveredMacroTypedefAlias<'tree>> {
13594    let type_node = fragmented_parenthesized_typedef_type(node)?;
13595    if type_node.kind() != "type_identifier"
13596        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(type_node, source)))
13597    {
13598        return None;
13599    }
13600
13601    let end_node = node.next_named_sibling()?;
13602    if end_node.kind() != "expression_statement" || end_node.named_child_count() != 1 {
13603        return None;
13604    }
13605    let name_node = end_node.named_child(0)?;
13606    if name_node.kind() != "identifier" {
13607        return None;
13608    }
13609    let has_terminator = (0..end_node.child_count()).any(|index| {
13610        end_node
13611            .child(index)
13612            .is_some_and(|child| child.kind() == ";" && !child.is_missing())
13613    });
13614    if !has_terminator {
13615        return None;
13616    }
13617    let name = normalize_cpp_whitespace(node_text(name_node, source));
13618    (!name.is_empty()).then_some(RecoveredMacroTypedefAlias { name, end_node })
13619}
13620
13621fn fragmented_parenthesized_typedef_type(node: Node<'_>) -> Option<Node<'_>> {
13622    if node.kind() != "type_definition" {
13623        return None;
13624    }
13625    let mut declarator_cursor = node.walk();
13626    let mut declarators = node.children_by_field_name("declarator", &mut declarator_cursor);
13627    if declarators.next()?.kind() != "parenthesized_declarator" || declarators.next().is_some() {
13628        return None;
13629    }
13630    let has_missing_terminator = (0..node.child_count()).any(|index| {
13631        node.child(index)
13632            .is_some_and(|child| child.kind() == ";" && child.is_missing())
13633    });
13634    if !has_missing_terminator {
13635        return None;
13636    }
13637    node.child_by_field_name("type")
13638}
13639
13640pub(crate) fn extract_typedef_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
13641    let mut stack = vec![node];
13642    while let Some(node) = stack.pop() {
13643        let child = match node.kind() {
13644            "identifier" | "field_identifier" | "type_identifier" => {
13645                return direct_identifier_name(node, source);
13646            }
13647            "qualified_identifier" => node.child_by_field_name("name")?,
13648            _ => node
13649                .child_by_field_name("declarator")
13650                .or_else(|| node.child_by_field_name("name"))
13651                .or_else(|| last_named_child(node))?,
13652        };
13653        stack.push(child);
13654    }
13655    None
13656}
13657
13658fn extract_macro_name(node: Node<'_>, source: &str) -> Option<String> {
13659    if let Some(name_node) = node.child_by_field_name("name") {
13660        let name = normalize_cpp_whitespace(node_text(name_node, source));
13661        return (!name.is_empty()).then_some(name);
13662    }
13663    let mut cursor = node.walk();
13664    node.named_children(&mut cursor)
13665        .find(|child| {
13666            matches!(
13667                child.kind(),
13668                "identifier" | "field_identifier" | "type_identifier"
13669            )
13670        })
13671        .and_then(|name_node| direct_identifier_name(name_node, source))
13672}
13673
13674fn render_cpp_type_signature(
13675    node: Node<'_>,
13676    source: &str,
13677    template_signature: Option<&str>,
13678) -> String {
13679    let text = normalize_cpp_whitespace(node_text(node, source));
13680    let head = text.split('{').next().unwrap_or(text.as_str()).trim();
13681    let rendered = if head.ends_with(';') {
13682        head.to_string()
13683    } else {
13684        format!("{head} {{")
13685    };
13686    if let Some(template_signature) = template_signature {
13687        format!("template {template_signature} {rendered}")
13688    } else {
13689        rendered
13690    }
13691}
13692
13693fn render_cpp_field_signature(node: Node<'_>, declarator: Node<'_>, source: &str) -> String {
13694    if let Some(recovered) = recovered_pyobject_head_field(node, source)
13695        && recovered.declarator == declarator
13696    {
13697        let type_text = normalize_cpp_whitespace(node_text(recovered.type_node, source));
13698        let declarator = normalize_cpp_whitespace(node_text(recovered.declarator, source));
13699        return format!("{type_text} {declarator};");
13700    }
13701    if let Some(recovered) = recovered_function_like_field_declarator(node, source)
13702        && recovered.name == declarator
13703    {
13704        let type_text = node
13705            .child_by_field_name("type")
13706            .map(|type_node| normalize_cpp_whitespace(node_text(type_node, source)))
13707            .unwrap_or_default();
13708        let name = normalize_cpp_whitespace(node_text(recovered.name, source));
13709        let mut prefix = String::new();
13710        let mut suffix = String::new();
13711        let mut current = recovered.declarator;
13712        while current.kind() != "function_declarator" {
13713            match current.kind() {
13714                "pointer_declarator" => prefix.push('*'),
13715                "reference_declarator" => prefix.push('&'),
13716                "array_declarator" => {
13717                    let size = current
13718                        .child_by_field_name("size")
13719                        .map(|size| normalize_cpp_whitespace(node_text(size, source)))
13720                        .unwrap_or_default();
13721                    suffix.push('[');
13722                    suffix.push_str(&size);
13723                    suffix.push(']');
13724                }
13725                "parenthesized_declarator" => {}
13726                _ => unreachable!("validated recovered field declarator wrapper"),
13727            }
13728            current = current
13729                .child_by_field_name("declarator")
13730                .expect("recovered field wrapper has an inner declarator");
13731        }
13732        let separator = if prefix.is_empty() { "" } else { " " };
13733        return format!("{type_text} {prefix}{separator}{name}{suffix};");
13734    }
13735    if let Some(signature) =
13736        render_recovered_macro_qualified_field_signature(node, declarator, source)
13737    {
13738        return signature;
13739    }
13740    let prefix = cpp_declaration_prefix(node, source);
13741    let name = extract_variable_name(declarator, source).unwrap_or_default();
13742    let raw_suffix = cpp_declarator_suffix_without_name(declarator, source);
13743    let suffix = if (prefix.ends_with('*') && raw_suffix == "*")
13744        || (prefix.ends_with('&') && raw_suffix == "&")
13745    {
13746        String::new()
13747    } else {
13748        raw_suffix
13749    };
13750
13751    let mut rendered = if suffix.is_empty() {
13752        format!("{prefix} {name}")
13753    } else if suffix.starts_with('*') || suffix.starts_with('&') {
13754        format!("{prefix}{suffix} {name}")
13755    } else if suffix.starts_with('[') || suffix.starts_with('(') {
13756        format!("{prefix} {name}{suffix}")
13757    } else {
13758        format!("{prefix} {suffix}{name}")
13759    };
13760    rendered = collapse_cpp_whitespace(&rendered);
13761
13762    // Check the terminal token directly instead of normalizing the whole
13763    // declaration, which can copy a large initializer for each field.
13764    if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13765        format!("{rendered} = {initializer};")
13766    } else if cpp_direct_semicolon(node).is_some() {
13767        format!("{rendered};")
13768    } else {
13769        rendered
13770    }
13771}
13772
13773fn render_recovered_macro_qualified_field_signature(
13774    node: Node<'_>,
13775    declarator: Node<'_>,
13776    source: &str,
13777) -> Option<String> {
13778    let recovered = recovered_macro_qualified_field_declarators(node, source)?;
13779    if !recovered
13780        .iter()
13781        .any(|candidate| same_node(*candidate, declarator))
13782    {
13783        return None;
13784    }
13785    let pseudo_declarator = node.child_by_field_name("declarator")?;
13786    let mut cursor = node.walk();
13787    let clause = node
13788        .named_children(&mut cursor)
13789        .find(|child| child.kind() == "bitfield_clause")?;
13790    let mut cursor = clause.walk();
13791    let error = clause
13792        .named_children(&mut cursor)
13793        .find(|child| child.kind() == "ERROR")?;
13794    let qualified_type =
13795        normalize_cpp_whitespace(source.get(pseudo_declarator.start_byte()..error.end_byte())?);
13796    let prefix = cpp_declaration_prefix(node, source);
13797    let name = extract_variable_name(declarator, source)?;
13798    let suffix = cpp_recovered_expression_declarator_suffix(declarator, source);
13799    let mut rendered = if suffix.is_empty() {
13800        format!("{prefix} {qualified_type} {name}")
13801    } else {
13802        format!("{prefix} {qualified_type} {suffix} {name}")
13803    };
13804    rendered = collapse_cpp_whitespace(&rendered);
13805
13806    if let Some(initializer) = recovered_macro_qualified_field_initializer(clause, declarator) {
13807        Some(format!(
13808            "{rendered} = {};",
13809            normalize_cpp_whitespace(node_text(initializer, source))
13810        ))
13811    } else if let Some(initializer) = cpp_preserved_initializer(node, declarator, source) {
13812        Some(format!("{rendered} = {initializer};"))
13813    } else {
13814        Some(format!("{rendered};"))
13815    }
13816}
13817
13818fn cpp_recovered_expression_declarator_suffix(node: Node<'_>, source: &str) -> String {
13819    match node.kind() {
13820        "pointer_expression" => {
13821            let operator = node
13822                .child_by_field_name("operator")
13823                .or_else(|| node.child(0))
13824                .map(|operator| node_text(operator, source))
13825                .unwrap_or("*");
13826            let argument = node
13827                .child_by_field_name("argument")
13828                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13829                .unwrap_or_default();
13830            format!("{operator}{argument}")
13831        }
13832        "unary_expression" => {
13833            let operator = node
13834                .child_by_field_name("operator")
13835                .or_else(|| node.child(0))
13836                .map(|operator| node_text(operator, source))
13837                .unwrap_or_default();
13838            let argument = node
13839                .child_by_field_name("argument")
13840                .map(|argument| cpp_recovered_expression_declarator_suffix(argument, source))
13841                .unwrap_or_default();
13842            format!("{operator}{argument}")
13843        }
13844        "identifier" | "field_identifier" => String::new(),
13845        _ => cpp_declarator_suffix_without_name(node, source),
13846    }
13847}
13848
13849fn recovered_macro_qualified_field_initializer<'tree>(
13850    clause: Node<'tree>,
13851    declarator: Node<'tree>,
13852) -> Option<Node<'tree>> {
13853    let mut stack = vec![clause];
13854    while let Some(current) = stack.pop() {
13855        if current.kind() == "assignment_expression"
13856            && current
13857                .child_by_field_name("left")
13858                .is_some_and(|left| same_node(left, declarator))
13859        {
13860            return current.child_by_field_name("right");
13861        }
13862        let mut cursor = current.walk();
13863        stack.extend(current.named_children(&mut cursor));
13864    }
13865    None
13866}
13867
13868fn cpp_declaration_prefix(node: Node<'_>, source: &str) -> String {
13869    let text = node_text(node, source);
13870    let mut cursor = node.walk();
13871    let first_declarator = node.named_children(&mut cursor).find(|child| {
13872        matches!(
13873            child.kind(),
13874            "init_declarator"
13875                | "identifier"
13876                | "field_identifier"
13877                | "pointer_declarator"
13878                | "reference_declarator"
13879                | "array_declarator"
13880                | "function_declarator"
13881        )
13882    });
13883    let prefix = if let Some(first_declarator) = first_declarator {
13884        let end = first_declarator
13885            .start_byte()
13886            .saturating_sub(node.start_byte());
13887        let mut prefix = text.get(..end).unwrap_or(text).to_string();
13888        let declarator_suffix = match first_declarator.kind() {
13889            "init_declarator" => first_declarator
13890                .child_by_field_name("declarator")
13891                .map(|inner| cpp_declarator_suffix_without_name(inner, source))
13892                .unwrap_or_default(),
13893            _ => cpp_declarator_suffix_without_name(first_declarator, source),
13894        };
13895        if declarator_suffix.starts_with('*') || declarator_suffix.starts_with('&') {
13896            prefix.push_str(&declarator_suffix);
13897        }
13898        return collapse_cpp_whitespace(&prefix)
13899            .trim_end_matches(',')
13900            .trim_end_matches(';')
13901            .trim()
13902            .to_string();
13903    } else {
13904        text
13905    };
13906    collapse_cpp_whitespace(prefix)
13907        .trim_end_matches(',')
13908        .trim_end_matches(';')
13909        .trim()
13910        .to_string()
13911}
13912
13913fn cpp_preserved_initializer(
13914    declaration_node: Node<'_>,
13915    declarator: Node<'_>,
13916    source: &str,
13917) -> Option<String> {
13918    let name = extract_variable_name(declarator, source)?;
13919    let mut cursor = declaration_node.walk();
13920    for child in declaration_node.named_children(&mut cursor) {
13921        if child.kind() != "init_declarator" {
13922            continue;
13923        }
13924        let Some(inner) = child.child_by_field_name("declarator") else {
13925            continue;
13926        };
13927        if extract_variable_name(inner, source).as_deref() != Some(name.as_str()) {
13928            continue;
13929        }
13930        let value = child.child_by_field_name("value")?;
13931        let kind = value.kind();
13932        if matches!(
13933            kind,
13934            "number_literal" | "float_literal" | "char_literal" | "true" | "false"
13935        ) {
13936            return Some(normalize_cpp_whitespace(node_text(value, source)));
13937        }
13938        break;
13939    }
13940    // The current grammar keeps a member initializer as a `default_value`
13941    // field beside the declarator rather than inside an `init_declarator`.
13942    if let Some(equals) = declarator.next_sibling().filter(|node| node.kind() == "=")
13943        && let Some(value) = equals.next_sibling().filter(|node| {
13944            declaration_node
13945                .child_by_field_name("default_value")
13946                .is_some_and(|default| same_node(default, *node))
13947        })
13948    {
13949        let kind = value.kind();
13950        if matches!(
13951            kind,
13952            "number_literal"
13953                | "float_literal"
13954                | "char_literal"
13955                | "true"
13956                | "false"
13957                | "null"
13958                | "nullptr"
13959                | "identifier"
13960        ) {
13961            return Some(normalize_cpp_whitespace(node_text(value, source)));
13962        }
13963    }
13964    let declaration_text = normalize_cpp_whitespace(node_text(declaration_node, source));
13965    let pattern = format!(
13966        r"\b{}\s*=\s*([-+]?[0-9]+(?:\.[0-9]+)?)",
13967        regex::escape(&name)
13968    );
13969    Regex::new(&pattern)
13970        .ok()
13971        .and_then(|regex| regex.captures(&declaration_text))
13972        .and_then(|captures| captures.get(1))
13973        .map(|value| value.as_str().to_string())
13974}
13975
13976fn render_cpp_function_display_signature_from_node<'tree>(
13977    node: Node<'tree>,
13978    source: &str,
13979    template_signature: Option<&str>,
13980    has_body: bool,
13981    ancestry: &ParentIndex<'tree>,
13982) -> String {
13983    let root = enclosing_cpp_declaration_node(node, ancestry).unwrap_or(node);
13984    let parent_text = node_text(root, source);
13985    let body_local_start = root
13986        .child_by_field_name("body")
13987        .map(|body| body.start_byte().saturating_sub(root.start_byte()))
13988        .unwrap_or(parent_text.len());
13989    let display = parent_text
13990        .get(..body_local_start)
13991        .unwrap_or(parent_text)
13992        .trim()
13993        .trim();
13994    let display = if let Some(template_signature) = template_signature {
13995        if display.starts_with("template ") {
13996            display.to_string()
13997        } else {
13998            format!("template {template_signature} {display}")
13999        }
14000    } else {
14001        display.to_string()
14002    };
14003    let display = collapse_cpp_whitespace(display.trim_end_matches(';'));
14004    if has_body {
14005        format!("{display} {{...}}")
14006    } else {
14007        format!("{display};")
14008    }
14009}
14010
14011fn cpp_template_signature(
14012    template_node: Node<'_>,
14013    declaration_child: Node<'_>,
14014    source: &str,
14015) -> Option<String> {
14016    let text = source
14017        .get(template_node.start_byte()..declaration_child.start_byte())
14018        .unwrap_or("");
14019    let text = normalize_cpp_whitespace(text);
14020    let start = text.find('<')?;
14021    let end = text.rfind('>')?;
14022    if end < start {
14023        return None;
14024    }
14025    Some(text[start..=end].to_string())
14026}
14027
14028struct RecoveredFragmentedPartialSpecialization<'tree> {
14029    declaration_node: Node<'tree>,
14030    name: String,
14031    range: Range,
14032    prefix_members: Vec<Node<'tree>>,
14033    member_siblings: Vec<Node<'tree>>,
14034    following_declarations: Vec<Node<'tree>>,
14035}
14036
14037struct RecoveredFragmentedPreprocessorClass<'tree> {
14038    declaration_node: Node<'tree>,
14039    class_node: Node<'tree>,
14040    body: Node<'tree>,
14041    name: String,
14042    range: Range,
14043    tail_members: Vec<Node<'tree>>,
14044    member_siblings: Vec<Node<'tree>>,
14045}
14046
14047/// Recover a class whose preprocessor-fragmented parse closes at an early
14048/// member body and publishes the remaining in-class declarations as siblings
14049/// of the surrounding alternative. Primary classes are admitted only when an
14050/// earlier branch contains the matching bodyless declaration and the class
14051/// node retains the displaced `#endif`. Partial specializations instead carry
14052/// their identity structurally in the `template_type` name and template
14053/// metadata. Retain the original AST nodes and re-own only the siblings through
14054/// the displaced structural `};` terminator.
14055fn recover_fragmented_preprocessor_class<'tree>(
14056    template_node: Node<'tree>,
14057    source: &str,
14058    ancestry: &ParentIndex<'tree>,
14059) -> Option<RecoveredFragmentedPreprocessorClass<'tree>> {
14060    let alternative = ancestry.parent(template_node)?;
14061    if alternative.kind() != "preproc_else" {
14062        return None;
14063    }
14064    let conditional = alternative.parent()?;
14065    if conditional.kind() != "preproc_if" {
14066        return None;
14067    }
14068    let declaration_node = template_node
14069        .named_children(&mut template_node.walk())
14070        .find(|child| matches!(child.kind(), "declaration" | "function_definition"))?;
14071    let class_node = declaration_node
14072        .named_children(&mut declaration_node.walk())
14073        .find(|child| matches!(child.kind(), "class_specifier" | "struct_specifier"))?;
14074    let body = cpp_body_node(class_node)?;
14075    if class_node.end_byte() >= declaration_node.end_byte() {
14076        return None;
14077    }
14078    let name = class_like_name(class_node, source, ancestry)?;
14079    let is_partial_specialization = class_node
14080        .child_by_field_name("name")
14081        .is_some_and(|class_name| class_name.kind() == "template_type");
14082    if is_partial_specialization {
14083        let metadata = cpp_template_metadata(template_node, class_node, source, ancestry)?;
14084        if metadata.specialization_arguments.is_empty() || !class_node.has_error() {
14085            return None;
14086        }
14087    } else {
14088        if !class_has_displaced_preprocessor_terminator(class_node) {
14089            return None;
14090        }
14091        let matching_other_branch = conditional
14092            .named_children(&mut conditional.walk())
14093            .take_while(|child| !same_node(*child, alternative))
14094            .filter(|child| child.kind() == "template_declaration")
14095            .filter_map(first_class_like_child)
14096            .any(|candidate| {
14097                cpp_body_node(candidate).is_none()
14098                    && class_like_name(candidate, source, ancestry).as_deref()
14099                        == Some(name.as_str())
14100            });
14101        if !matching_other_branch {
14102            return None;
14103        }
14104    }
14105
14106    let mut tail_members = Vec::new();
14107    let mut saw_class = false;
14108    let mut declaration_cursor = declaration_node.walk();
14109    for child in declaration_node.named_children(&mut declaration_cursor) {
14110        if same_node(child, class_node) {
14111            saw_class = true;
14112        } else if saw_class {
14113            tail_members.push(child);
14114        }
14115    }
14116
14117    let mut member_siblings = Vec::new();
14118    let mut saw_template = false;
14119    let mut terminator = None;
14120    for index in 0..alternative.child_count() {
14121        let Some(child) = alternative.child(index) else {
14122            continue;
14123        };
14124        if same_node(child, template_node) {
14125            saw_template = true;
14126            continue;
14127        }
14128        if !saw_template {
14129            continue;
14130        }
14131        if displaced_fragmented_class_terminator(alternative, index) {
14132            terminator = alternative.child(index + 1);
14133            break;
14134        }
14135        if child.is_named() {
14136            member_siblings.push(child);
14137        }
14138    }
14139    let terminator = terminator?;
14140    Some(RecoveredFragmentedPreprocessorClass {
14141        declaration_node,
14142        class_node,
14143        body,
14144        name,
14145        range: Range {
14146            start_byte: class_node.start_byte(),
14147            end_byte: terminator.end_byte(),
14148            start_line: class_node.start_position().row + 1,
14149            end_line: terminator.end_position().row + 1,
14150        },
14151        tail_members,
14152        member_siblings,
14153    })
14154}
14155
14156fn class_has_displaced_preprocessor_terminator(class_node: Node<'_>) -> bool {
14157    (0..class_node.child_count()).any(|index| {
14158        class_node.child(index).is_some_and(|child| {
14159            child.kind() == "ERROR"
14160                && (0..child.child_count()).any(|error_index| {
14161                    child
14162                        .child(error_index)
14163                        .is_some_and(|token| token.kind() == "#endif")
14164                })
14165        })
14166    })
14167}
14168
14169/// The real `#endif` that tree-sitter consumed inside an error subtree.
14170///
14171/// A preprocessor directive inside a malformed array bound can cause later
14172/// declarations to remain children of the conditional. The non-missing token
14173/// still gives the exact structured boundary. Ignore nested conditionals and
14174/// select the last unpaired error-owned token. Tree-sitter can pair a later
14175/// outer `#endif` with this conditional, so the direct terminator is not
14176/// necessarily missing.
14177pub fn cpp_displaced_preprocessor_terminator<'tree>(
14178    conditional: Node<'tree>,
14179) -> Option<Node<'tree>> {
14180    if !conditional.has_error() {
14181        return None;
14182    }
14183    let has_concrete_direct_terminator = conditional
14184        .child_count()
14185        .checked_sub(1)
14186        .and_then(|index| conditional.child(index))
14187        .is_some_and(|child| child.kind() == "#endif" && !child.is_missing());
14188    if has_concrete_direct_terminator && conditional.child_by_field_name("alternative").is_some() {
14189        // A structured alternative proves that the direct `#endif` closes
14190        // this family. An error-owned terminator inside either branch belongs
14191        // to a damaged nested conditional, not to this one.
14192        return None;
14193    }
14194    // Pair concrete directive tokens while walking the recovered subtree. An
14195    // inner conditional may be represented only by its `#ifndef`/`#endif`
14196    // tokens under an `ERROR`, so skipping nested preprocessor nodes is not
14197    // sufficient: an inner `#endif` can otherwise be mistaken for this
14198    // conditional's displaced terminator.
14199    let mut displaced = None;
14200    let mut conditional_depth = 0usize;
14201    let mut stack = children_iter(conditional)
14202        .map(|child| (child, false))
14203        .collect::<Vec<_>>();
14204    stack.reverse();
14205    while let Some((node, inside_error)) = stack.pop() {
14206        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14207            continue;
14208        }
14209        if node != conditional
14210            && matches!(
14211                node.kind(),
14212                "preproc_if" | "preproc_ifdef" | "preproc_ifndef" | "preproc_elif"
14213            )
14214        {
14215            continue;
14216        }
14217        let inside_error = inside_error || node.kind() == "ERROR";
14218        match node.kind() {
14219            "#if" | "#ifdef" | "#ifndef" if node.start_byte() != conditional.start_byte() => {
14220                conditional_depth += 1;
14221            }
14222            "#endif" if !node.is_missing() => {
14223                if conditional_depth == 0
14224                    && inside_error
14225                    && displaced
14226                        .is_none_or(|current: Node<'_>| node.end_byte() > current.end_byte())
14227                {
14228                    displaced = Some(node);
14229                }
14230                conditional_depth = conditional_depth.saturating_sub(1);
14231            }
14232            _ => {}
14233        }
14234        let first_pushed = stack.len();
14235        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14236        stack[first_pushed..].reverse();
14237    }
14238    displaced
14239}
14240
14241/// The first `#else`/`#elif` of `conditional` that tree-sitter displaced into
14242/// an `ERROR` inside the conditional's own branch.
14243///
14244/// The macro-declaration grammar can read an unterminated end sentinel, the
14245/// branch directive and the next branch's begin sentinel as one declaration
14246/// (`END_NS #else BEGIN_NS class C {};`, #6325): the `#else` becomes an
14247/// `ERROR` child and the conditional loses its `alternative`. The token is
14248/// still the structured branch boundary. Nested conditionals own their own
14249/// directives, including ones represented only by error-owned `#if`/`#endif`
14250/// tokens, so the walk pairs those the same way
14251/// [`cpp_displaced_preprocessor_terminator`] does.
14252pub fn cpp_displaced_preprocessor_branch<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14253    if !conditional.has_error() {
14254        return None;
14255    }
14256    let alternative = conditional.child_by_field_name("alternative");
14257    let mut conditional_depth = 0usize;
14258    let mut stack = children_iter(conditional)
14259        .filter(|child| Some(*child) != alternative)
14260        .map(|child| (child, false))
14261        .collect::<Vec<_>>();
14262    stack.reverse();
14263    while let Some((node, inside_error)) = stack.pop() {
14264        if !inside_error && node.kind() != "ERROR" && !node.has_error() {
14265            continue;
14266        }
14267        if matches!(
14268            node.kind(),
14269            "preproc_if"
14270                | "preproc_ifdef"
14271                | "preproc_ifndef"
14272                | "preproc_elif"
14273                | "preproc_elifdef"
14274                | "preproc_else"
14275        ) {
14276            continue;
14277        }
14278        let inside_error = inside_error || node.kind() == "ERROR";
14279        match node.kind() {
14280            "#if" | "#ifdef" | "#ifndef" => conditional_depth += 1,
14281            "#endif" if !node.is_missing() => {
14282                conditional_depth = conditional_depth.saturating_sub(1);
14283            }
14284            "#else" | "#elif" | "#elifdef" | "#elifndef"
14285                if inside_error && conditional_depth == 0 && !node.is_missing() =>
14286            {
14287                return Some(node);
14288            }
14289            _ => {}
14290        }
14291        let first_pushed = stack.len();
14292        stack.extend(children_iter(node).map(|child| (child, inside_error)));
14293        stack[first_pushed..].reverse();
14294    }
14295    None
14296}
14297
14298/// The effective end of a conditional whose real terminator tree-sitter
14299/// displaced into declaration recovery.
14300///
14301/// Most damaged conditionals retain a concrete `#endif` token below an
14302/// `ERROR`; [`cpp_displaced_preprocessor_terminator`] supplies that exact
14303/// boundary. A preprocessor family that selects the middle of a declaration
14304/// can lose the directive tokens entirely. In that shape tree-sitter leaves
14305/// the declaration's `typedef` token as the sole child of the immediately
14306/// preceding top-level `ERROR`, and puts a multiline `ERROR` plus the trailing
14307/// declarator name inside the conditional's first declaration. The declaration
14308/// end is then the smallest structured boundary that contains the whole split
14309/// declaration.
14310#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14311pub struct CppDisplacedPreprocessorBoundary {
14312    pub end_byte: usize,
14313    pub end_line: usize,
14314}
14315
14316pub fn cpp_displaced_preprocessor_boundary(
14317    conditional: Node<'_>,
14318) -> Option<CppDisplacedPreprocessorBoundary> {
14319    if let Some(terminator) = displaced_declaration_prefix_terminator(conditional) {
14320        return Some(CppDisplacedPreprocessorBoundary {
14321            end_byte: terminator.end_byte(),
14322            end_line: terminator.end_position().row + 1,
14323        });
14324    }
14325    if let Some(declaration) = displaced_split_declaration(conditional) {
14326        return Some(CppDisplacedPreprocessorBoundary {
14327            end_byte: declaration.end_byte(),
14328            end_line: declaration.end_position().row + 1,
14329        });
14330    }
14331    if let Some(terminator) = displaced_nested_conditional_terminator(conditional) {
14332        return Some(CppDisplacedPreprocessorBoundary {
14333            end_byte: terminator.end_byte(),
14334            end_line: terminator.end_position().row + 1,
14335        });
14336    }
14337    if let Some(terminator) = cpp_displaced_preprocessor_terminator(conditional) {
14338        return Some(CppDisplacedPreprocessorBoundary {
14339            end_byte: terminator.end_byte(),
14340            end_line: terminator.end_position().row + 1,
14341        });
14342    }
14343    None
14344}
14345
14346/// Recover an outer terminator that tree-sitter assigned to a damaged nested
14347/// conditional. This occurs when a split construct such as `extern "C"`
14348/// consumes the nested `#endif` inside an error node: the nested conditional's
14349/// direct terminator is then the outer conditional's real terminator, while
14350/// the outer node ends with a missing token and absorbs later declarations.
14351fn displaced_nested_conditional_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14352    if !conditional.has_error()
14353        || conditional.child_by_field_name("alternative").is_some()
14354        || conditional
14355            .child(conditional.child_count().saturating_sub(1))
14356            .is_none_or(|child| child.kind() != "#endif" || !child.is_missing())
14357    {
14358        return None;
14359    }
14360    let mut recovered = None;
14361    for nested in named_children_iter(conditional) {
14362        if !matches!(
14363            nested.kind(),
14364            "preproc_if" | "preproc_ifdef" | "preproc_ifndef"
14365        ) || nested.child_by_field_name("alternative").is_some()
14366        {
14367            continue;
14368        }
14369        let Some(direct) = nested.child(nested.child_count().saturating_sub(1)) else {
14370            continue;
14371        };
14372        if direct.kind() != "#endif" || direct.is_missing() {
14373            continue;
14374        }
14375        let Some(displaced) = cpp_displaced_preprocessor_terminator(nested) else {
14376            continue;
14377        };
14378        if displaced.end_byte() >= direct.start_byte() {
14379            continue;
14380        }
14381        if recovered.is_none_or(|current: Node<'_>| direct.end_byte() > current.end_byte()) {
14382            recovered = Some(direct);
14383        }
14384    }
14385    recovered
14386}
14387
14388fn displaced_declaration_prefix_terminator<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14389    if !conditional.has_error() || conditional.child_by_field_name("alternative").is_some() {
14390        return None;
14391    }
14392    let mut cursor = conditional.walk();
14393    let declarations = conditional
14394        .named_children(&mut cursor)
14395        .filter(|child| matches!(child.kind(), "declaration" | "function_definition"))
14396        .collect::<Vec<_>>();
14397    let declaration = *declarations.first()?;
14398    if declaration.end_byte() >= conditional.end_byte() || declarations.len() < 2 {
14399        return None;
14400    }
14401    let declarator_start = declaration.child_by_field_name("declarator")?.start_byte();
14402    let mut terminator = None;
14403    let mut stack = (0..declaration.child_count())
14404        .filter_map(|index| declaration.child(index))
14405        .filter(|child| child.start_byte() < declarator_start)
14406        .map(|child| (child, false))
14407        .collect::<Vec<_>>();
14408    while let Some((node, inside_error)) = stack.pop() {
14409        let inside_error = inside_error || node.kind() == "ERROR";
14410        if inside_error && node.kind() == "#endif" && !node.is_missing() {
14411            terminator = Some(node);
14412            continue;
14413        }
14414        for index in 0..node.child_count() {
14415            if let Some(child) = node.child(index)
14416                && child.start_byte() < declarator_start
14417            {
14418                stack.push((child, inside_error));
14419            }
14420        }
14421    }
14422    terminator
14423}
14424
14425fn displaced_split_declaration<'tree>(conditional: Node<'tree>) -> Option<Node<'tree>> {
14426    if !conditional.has_error()
14427        || conditional.child_by_field_name("alternative").is_some()
14428        || conditional
14429            .prev_named_sibling()
14430            .filter(|sibling| {
14431                sibling.kind() == "ERROR"
14432                    && sibling.child_count() == 1
14433                    && sibling
14434                        .child(0)
14435                        .is_some_and(|child| child.kind() == "typedef")
14436            })
14437            .filter(|sibling| sibling.end_position().row + 1 == conditional.start_position().row)
14438            .is_none()
14439    {
14440        return None;
14441    }
14442    let mut cursor = conditional.walk();
14443    let children = conditional.named_children(&mut cursor).collect::<Vec<_>>();
14444    let declaration_index = children
14445        .iter()
14446        .position(|child| child.kind() == "declaration" && child.has_error())?;
14447    let declaration = children[declaration_index];
14448    if !children
14449        .iter()
14450        .skip(declaration_index + 1)
14451        .any(|child| child.end_byte() > declaration.end_byte())
14452    {
14453        return None;
14454    }
14455    let declarator = declaration.child_by_field_name("declarator")?;
14456    let mut error_end = None;
14457    let mut names = Vec::new();
14458    let mut stack = vec![declarator];
14459    while let Some(node) = stack.pop() {
14460        if node.kind() == "ERROR" && node.end_position().row > node.start_position().row {
14461            error_end =
14462                Some(error_end.map_or(node.end_byte(), |end: usize| end.max(node.end_byte())));
14463            continue;
14464        }
14465        if matches!(node.kind(), "identifier" | "type_identifier") {
14466            names.push(node.start_byte());
14467        }
14468        push_named_children_reversed(node, &mut stack);
14469    }
14470    let error_end = error_end?;
14471    names
14472        .into_iter()
14473        .any(|start| start >= error_end)
14474        .then_some(declaration)
14475}
14476
14477fn displaced_fragmented_class_terminator(parent: Node<'_>, error_index: usize) -> bool {
14478    let Some(error) = parent.child(error_index) else {
14479        return false;
14480    };
14481    if error.kind() != "ERROR"
14482        || error.child_count() != 1
14483        || error.child(0).is_none_or(|child| child.kind() != "}")
14484    {
14485        return false;
14486    }
14487    let Some(semicolon) = parent.child(error_index + 1) else {
14488        return false;
14489    };
14490    semicolon.kind() == "expression_statement"
14491        && semicolon.child_count() == 1
14492        && semicolon.child(0).is_some_and(|child| child.kind() == ";")
14493}
14494
14495/// Locate the real end of a class-like declaration when a macro invocation
14496/// without a source semicolon absorbs the class's `};` into its parsed field.
14497/// The grammar then keeps following namespace declarations as later children
14498/// of the same field list. The direct ERROR-plus-semicolon pair proves the
14499/// boundary structurally; no source-text delimiter scan is needed.
14500fn displaced_macro_class_tail(
14501    declaration_node: Node<'_>,
14502    body: Node<'_>,
14503    source: &str,
14504) -> Option<DisplacedMacroClassTail> {
14505    if !matches!(
14506        declaration_node.kind(),
14507        "class_specifier" | "struct_specifier" | "union_specifier"
14508    ) || body.kind() != "field_declaration_list"
14509    {
14510        return None;
14511    }
14512
14513    let child_count = body.named_child_count();
14514    for index in 0..child_count {
14515        let child = body.named_child(index)?;
14516        let Some(terminator) = displaced_macro_field_terminator(child, source) else {
14517            continue;
14518        };
14519        let split_index = index + 1;
14520        if split_index >= child_count {
14521            return None;
14522        }
14523        let mut cursor = body.walk();
14524        if !body
14525            .named_children(&mut cursor)
14526            .skip(split_index)
14527            .any(|tail| cpp_is_indexable_item_kind(tail.kind()))
14528        {
14529            return None;
14530        }
14531        return Some(DisplacedMacroClassTail {
14532            split_index,
14533            class_range: Range {
14534                start_byte: declaration_node.start_byte(),
14535                end_byte: terminator.end_byte(),
14536                start_line: declaration_node.start_position().row + 1,
14537                end_line: terminator.end_position().row + 1,
14538            },
14539        });
14540    }
14541    None
14542}
14543
14544fn displaced_macro_field_terminator<'tree>(
14545    field: Node<'tree>,
14546    source: &str,
14547) -> Option<Node<'tree>> {
14548    if field.kind() != "field_declaration" {
14549        return None;
14550    }
14551    // The older grammar reads the unterminated `MACRO(arg)` invocation as a
14552    // macro-token type and a `parenthesized_declarator`; the macro-declaration
14553    // grammar reads the whole invocation as a `macro_type_specifier`.
14554    let macro_type = field.child_by_field_name("type")?;
14555    let macro_name = match macro_type.kind() {
14556        "type_identifier"
14557            if field.child_by_field_name("declarator")?.kind() == "parenthesized_declarator" =>
14558        {
14559            macro_type
14560        }
14561        "macro_type_specifier" => macro_type.child_by_field_name("name")?,
14562        _ => return None,
14563    };
14564    if !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source))) {
14565        return None;
14566    }
14567    for index in 0..field.child_count() {
14568        let error = field.child(index)?;
14569        if error.kind() != "ERROR"
14570            || error.child_count() != 1
14571            || error.child(0).is_none_or(|child| child.kind() != "}")
14572        {
14573            continue;
14574        }
14575        let semicolon = field.child(index + 1)?;
14576        if semicolon.kind() == ";" {
14577            return Some(semicolon);
14578        }
14579    }
14580    None
14581}
14582
14583fn recover_fragmented_partial_specialization<'tree>(
14584    template_node: Node<'tree>,
14585    declaration_child: Node<'tree>,
14586    source: &str,
14587    ancestry: &ParentIndex<'tree>,
14588) -> Option<RecoveredFragmentedPartialSpecialization<'tree>> {
14589    if declaration_child.kind() != "function_definition" {
14590        return None;
14591    }
14592    let class_node = declaration_child.child_by_field_name("type")?;
14593    if !matches!(
14594        class_node.kind(),
14595        "class_specifier" | "struct_specifier" | "union_specifier"
14596    ) || !class_node
14597        .child_by_field_name("name")
14598        .and_then(|name| direct_identifier_name(name, source))
14599        .is_some_and(|name| cpp_export_macro_token(&name))
14600    {
14601        return None;
14602    }
14603    let declarator = declaration_child.child_by_field_name("declarator")?;
14604    if declarator.kind() != "template_function" {
14605        return None;
14606    }
14607    let metadata = cpp_template_metadata(template_node, declaration_child, source, ancestry)?;
14608    if metadata.specialization_arguments.is_empty() {
14609        return None;
14610    }
14611    let body = declaration_child.child_by_field_name("body")?;
14612    if body.kind() != "compound_statement" {
14613        return None;
14614    }
14615    let complete_prefix = body.named_child(0).filter(|first| {
14616        first.kind() == "labeled_statement"
14617            && first.has_error()
14618            && first
14619                .named_child(first.named_child_count().saturating_sub(1))
14620                .is_some_and(recovered_declaration_has_class_terminator)
14621    });
14622    let complete_body = complete_prefix.is_some();
14623    let mut prefix_members = Vec::new();
14624    if let Some(prefix) = complete_prefix {
14625        prefix_members.push(prefix);
14626    } else {
14627        let mut body_cursor = body.walk();
14628        for child in body.named_children(&mut body_cursor) {
14629            if !is_structurally_valid_fragmented_class_prefix_member(child) {
14630                break;
14631            }
14632            prefix_members.push(child);
14633        }
14634    }
14635    let containing_declarations = template_node.parent()?;
14636    if !matches!(
14637        containing_declarations.kind(),
14638        "declaration_list" | "compound_statement"
14639    ) {
14640        return None;
14641    }
14642    let mut member_siblings = Vec::new();
14643    let mut following_declarations = Vec::new();
14644    let terminator;
14645    if complete_body {
14646        terminator = complete_prefix?;
14647        let mut cursor = body.walk();
14648        let mut after_prefix = false;
14649        for child in body.named_children(&mut cursor) {
14650            if complete_prefix.is_some_and(|prefix| same_node(child, prefix)) {
14651                after_prefix = true;
14652            } else if after_prefix {
14653                following_declarations.push(child);
14654            }
14655        }
14656    } else {
14657        let mut found_template = false;
14658        let mut cursor = containing_declarations.walk();
14659        let mut class_terminator = None;
14660        for child in containing_declarations.children(&mut cursor) {
14661            if same_node(child, template_node) {
14662                found_template = true;
14663                continue;
14664            }
14665            if found_template && child.kind() == "}" {
14666                class_terminator = Some(child);
14667                break;
14668            }
14669            // A namespace can never be a class member: reaching one before the
14670            // terminator proves the class's true close was swallowed upstream
14671            // and this scan has crossed into the enclosing scope, so the
14672            // recovery cannot be bounded -- continuing re-owns the namespace
14673            // block (and its template specializations) as class members under
14674            // a re-appended package, desyncing the fq boundary (#2306).
14675            if found_template && child.kind() == "namespace_definition" {
14676                return None;
14677            }
14678            if found_template && child.is_named() {
14679                member_siblings.push(child);
14680            }
14681        }
14682        terminator = class_terminator?;
14683    }
14684    let name = format!(
14685        "{}<{}>",
14686        metadata.primary_name,
14687        metadata
14688            .specialization_arguments
14689            .iter()
14690            .map(|argument| argument.text.as_str())
14691            .collect::<Vec<_>>()
14692            .join(", ")
14693    );
14694    Some(RecoveredFragmentedPartialSpecialization {
14695        declaration_node: declaration_child,
14696        name,
14697        range: Range {
14698            start_byte: declaration_child.start_byte(),
14699            end_byte: terminator.end_byte(),
14700            start_line: declaration_child.start_position().row + 1,
14701            end_line: terminator.end_position().row + 1,
14702        },
14703        prefix_members,
14704        member_siblings,
14705        following_declarations,
14706    })
14707}
14708
14709/// Whether `node` is the function-shaped parser wrapper whose body was
14710/// recovered as a fragmented partial-specialization class body.
14711///
14712/// Consumers that walk lexical function scopes must use the same complete
14713/// structural proof as declaration extraction. Otherwise member callables in
14714/// the recovered body are mistaken for function-local declarations.
14715pub fn is_recovered_fragmented_partial_specialization_container(
14716    node: Node<'_>,
14717    source: &str,
14718) -> bool {
14719    let Some(template) = node
14720        .parent()
14721        .filter(|parent| parent.kind() == "template_declaration")
14722    else {
14723        return false;
14724    };
14725    let mut root = template;
14726    while let Some(parent) = root.parent() {
14727        root = parent;
14728    }
14729    recover_fragmented_partial_specialization(template, node, source, &ParentIndex::new(root))
14730        .is_some()
14731}
14732
14733fn recovered_declaration_has_class_terminator(declaration: Node<'_>) -> bool {
14734    if declaration.kind() != "declaration" {
14735        return false;
14736    }
14737    // With an export macro between `class` and its name, tree-sitter folds a
14738    // complete class body into a function-shaped declaration. The class's own
14739    // `};` remains structurally identifiable as a direct ERROR child holding
14740    // `}`, immediately followed by the declaration's direct `;` child.
14741    (0..declaration.child_count().saturating_sub(1)).any(|index| {
14742        let Some(error) = declaration.child(index) else {
14743            return false;
14744        };
14745        error.kind() == "ERROR"
14746            && error.child_count() == 1
14747            && error.child(0).is_some_and(|child| child.kind() == "}")
14748            && declaration
14749                .child(index + 1)
14750                .is_some_and(|child| child.kind() == ";")
14751    })
14752}
14753
14754fn is_structurally_valid_fragmented_class_prefix_member(node: Node<'_>) -> bool {
14755    if node.has_error() {
14756        return false;
14757    }
14758    match node.kind() {
14759        "declaration"
14760        | "field_declaration"
14761        | "alias_declaration"
14762        | "type_definition"
14763        | "static_assert_declaration" => true,
14764        "labeled_statement" => node
14765            .named_child(node.named_child_count().saturating_sub(1))
14766            .is_some_and(is_structurally_valid_fragmented_class_prefix_member),
14767        "template_declaration" => node.named_children(&mut node.walk()).any(|child| {
14768            matches!(
14769                child.kind(),
14770                "declaration"
14771                    | "field_declaration"
14772                    | "alias_declaration"
14773                    | "type_definition"
14774                    | "function_definition"
14775            )
14776        }),
14777        _ => false,
14778    }
14779}
14780
14781fn recovered_using_declaration_alias_name(node: Node<'_>, source: &str) -> Option<String> {
14782    (node.kind() == "declaration" && node.child(0)?.kind() == "using")
14783        .then(|| node.child_by_field_name("declarator"))
14784        .flatten()
14785        .and_then(|declarator| extract_variable_name(declarator, source))
14786}
14787
14788use crate::graph::syntax::has_function_scope_ancestor;
14789
14790fn cpp_template_metadata<'tree>(
14791    template_node: Node<'tree>,
14792    declaration_child: Node<'tree>,
14793    source: &str,
14794    ancestry: &ParentIndex<'tree>,
14795) -> Option<CppTemplateMetadata> {
14796    let parameters_node = template_node.child_by_field_name("parameters")?;
14797    let name_node = cpp_templated_class_name_node(declaration_child)?;
14798    let primary_node = match name_node.kind() {
14799        "template_type" | "template_function" => name_node.child_by_field_name("name")?,
14800        _ => name_node,
14801    };
14802    let primary_name = normalize_cpp_whitespace(node_text(primary_node, source));
14803    if primary_name.is_empty() || cpp_export_macro_token(&primary_name) {
14804        return None;
14805    }
14806
14807    let mut parameter_nodes = Vec::new();
14808    let mut parameter_names = Vec::new();
14809    let mut cursor = parameters_node.walk();
14810    for parameter in parameters_node.named_children(&mut cursor) {
14811        if !matches!(
14812            parameter.kind(),
14813            "type_parameter_declaration"
14814                | "optional_type_parameter_declaration"
14815                | "variadic_type_parameter_declaration"
14816                | "template_template_parameter_declaration"
14817                | "parameter_declaration"
14818                | "optional_parameter_declaration"
14819                | "variadic_parameter_declaration"
14820        ) {
14821            continue;
14822        }
14823        let index = parameter_nodes.len();
14824        // An unnamed parameter still contributes template arity and kind. Use
14825        // an impossible C++ identifier so positional reconciliation can bind
14826        // it without making source expressions refer to a name that was not
14827        // written.
14828        let name = cpp_template_parameter_name(parameter, source)
14829            .unwrap_or_else(|| format!("<anonymous:{index}>"));
14830        parameter_names.push(name);
14831        parameter_nodes.push(parameter);
14832    }
14833    let parameters = parameter_nodes
14834        .into_iter()
14835        .zip(parameter_names.iter().cloned())
14836        .map(|(parameter, name)| CppTemplateParameterMetadata {
14837            name,
14838            kind: cpp_template_parameter_kind(parameter),
14839            variadic: matches!(
14840                parameter.kind(),
14841                "variadic_type_parameter_declaration" | "variadic_parameter_declaration"
14842            ),
14843            default: cpp_template_parameter_default_expression(
14844                parameter,
14845                source,
14846                &parameter_names,
14847                ancestry,
14848            ),
14849        })
14850        .collect();
14851    let specialization_arguments = if declaration_child.kind() == "alias_declaration" {
14852        Vec::new()
14853    } else {
14854        cpp_template_argument_expressions(name_node, source, &parameter_names, ancestry)
14855            .unwrap_or_default()
14856    };
14857    let alias_target = (declaration_child.kind() == "alias_declaration")
14858        .then(|| cpp_template_alias_target(declaration_child, source, &parameter_names, ancestry))
14859        .flatten();
14860    Some(CppTemplateMetadata {
14861        primary_name,
14862        primary_fq_name: String::new(),
14863        parameters,
14864        specialization_arguments,
14865        alias_target,
14866    })
14867}
14868
14869fn cpp_templated_class_name_node(node: Node<'_>) -> Option<Node<'_>> {
14870    match node.kind() {
14871        "class_specifier" | "struct_specifier" | "union_specifier" => {
14872            node.child_by_field_name("name")
14873        }
14874        "function_definition" => {
14875            let declarator = node.child_by_field_name("declarator")?;
14876            if matches!(declarator.kind(), "identifier" | "template_function") {
14877                Some(declarator)
14878            } else {
14879                None
14880            }
14881        }
14882        "alias_declaration" => node.child_by_field_name("name"),
14883        _ => None,
14884    }
14885}
14886
14887fn cpp_template_alias_target<'tree>(
14888    alias: Node<'tree>,
14889    source: &str,
14890    parameter_names: &[String],
14891    ancestry: &ParentIndex<'tree>,
14892) -> Option<CppTemplateAliasTargetMetadata> {
14893    let mut type_node = alias.child_by_field_name("type")?;
14894    while type_node.kind() == "type_descriptor" {
14895        type_node = type_node.child_by_field_name("type")?;
14896    }
14897    let global = type_node.child_by_field_name("scope").is_none()
14898        && type_node.child(0).is_some_and(|child| child.kind() == "::");
14899    let mut components = Vec::new();
14900    cpp_template_target_components(type_node, source, &mut components)?;
14901    let arguments = cpp_template_argument_expressions(type_node, source, parameter_names, ancestry);
14902    (!components.is_empty()).then_some(CppTemplateAliasTargetMetadata {
14903        components,
14904        global,
14905        arguments,
14906    })
14907}
14908
14909fn cpp_template_target_components(
14910    node: Node<'_>,
14911    source: &str,
14912    out: &mut Vec<String>,
14913) -> Option<()> {
14914    // Delay reading the terminal name until the scope has been processed,
14915    // preserving the partial output even when a later component is missing.
14916    enum Component<'tree> {
14917        Node(Node<'tree>),
14918        Name(Node<'tree>),
14919    }
14920    let mut stack = vec![Component::Node(node)];
14921    while let Some(component) = stack.pop() {
14922        let node = match component {
14923            Component::Node(node) => node,
14924            Component::Name(node) => node.child_by_field_name("name")?,
14925        };
14926        match node.kind() {
14927            "identifier" | "namespace_identifier" | "type_identifier" => {
14928                out.push(node_text(node, source).to_string());
14929            }
14930            "template_type" => stack.push(Component::Name(node)),
14931            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
14932                stack.push(Component::Name(node));
14933                if let Some(scope) = node.child_by_field_name("scope") {
14934                    stack.push(Component::Node(scope));
14935                }
14936            }
14937            _ => return None,
14938        }
14939    }
14940    Some(())
14941}
14942
14943fn cpp_template_argument_expressions<'tree>(
14944    mut node: Node<'tree>,
14945    source: &str,
14946    parameter_names: &[String],
14947    ancestry: &ParentIndex<'tree>,
14948) -> Option<Vec<CppTemplateExpression>> {
14949    loop {
14950        match node.kind() {
14951            "template_type" | "template_function" => {
14952                let arguments = node.child_by_field_name("arguments")?;
14953                let mut cursor = arguments.walk();
14954                return Some(
14955                    arguments
14956                        .named_children(&mut cursor)
14957                        .filter(|argument| !argument.is_extra() && argument.kind() != "comment")
14958                        .map(|argument| {
14959                            cpp_template_expression(argument, source, parameter_names, ancestry)
14960                        })
14961                        .collect(),
14962                );
14963            }
14964            "qualified_identifier" | "scoped_type_identifier" | "type_descriptor" => {
14965                node = node
14966                    .child_by_field_name("name")
14967                    .or_else(|| node.child_by_field_name("type"))?;
14968            }
14969            _ => return None,
14970        }
14971    }
14972}
14973
14974fn cpp_template_parameter_declaration(node: Node<'_>) -> Node<'_> {
14975    if node.kind() == "template_template_parameter_declaration" {
14976        let mut cursor = node.walk();
14977        return node
14978            .named_children(&mut cursor)
14979            .find(|child| {
14980                matches!(
14981                    child.kind(),
14982                    "type_parameter_declaration"
14983                        | "optional_type_parameter_declaration"
14984                        | "variadic_type_parameter_declaration"
14985                )
14986            })
14987            .expect("a template-template parameter has a type parameter declaration");
14988    }
14989    node
14990}
14991
14992pub(crate) fn cpp_template_parameter_name(node: Node<'_>, source: &str) -> Option<String> {
14993    let node = cpp_template_parameter_declaration(node);
14994    let candidate = node
14995        .child_by_field_name("name")
14996        .or_else(|| node.child_by_field_name("declarator"))
14997        .or_else(|| cpp_recovered_type_parameter_name(node))
14998        .or_else(|| {
14999            let mut cursor = node.walk();
15000            node.named_children(&mut cursor).find(|child| {
15001                matches!(
15002                    child.kind(),
15003                    "identifier" | "type_identifier" | "field_identifier"
15004                )
15005            })
15006        })?;
15007    if let Some(name) = direct_identifier_name(candidate, source) {
15008        return Some(name);
15009    }
15010    let name = normalize_cpp_whitespace(&extract_declarator_name(candidate, source));
15011    (!name.is_empty()).then_some(name)
15012}
15013
15014pub(crate) fn cpp_template_parameter_kind(node: Node<'_>) -> CppTemplateParameterKind {
15015    match node.kind() {
15016        "type_parameter_declaration"
15017        | "optional_type_parameter_declaration"
15018        | "variadic_type_parameter_declaration" => CppTemplateParameterKind::Type,
15019        "template_template_parameter_declaration" => CppTemplateParameterKind::Template,
15020        _ if cpp_recovered_type_parameter_name(node).is_some() => CppTemplateParameterKind::Type,
15021        _ => CppTemplateParameterKind::Value,
15022    }
15023}
15024
15025/// The name of a defaulted type parameter that tree-sitter-cpp recovered as a
15026/// non-type parameter. The grammar's `default_type` cannot hold an abstract
15027/// declarator, so `typename U = T*` always recovers the `*` as an ERROR
15028/// sibling; since grammar 0.24.4 the parameter itself also becomes an
15029/// `optional_parameter_declaration` with `type: dependent_type(typename U)`,
15030/// no declarator and `default_value: T`. A valid dependent type names a
15031/// qualified type after `typename`, so a bare type identifier there is the
15032/// type parameter's name.
15033pub fn cpp_recovered_type_parameter_name(parameter: Node<'_>) -> Option<Node<'_>> {
15034    if parameter.kind() != "optional_parameter_declaration"
15035        || parameter.child_by_field_name("declarator").is_some()
15036    {
15037        return None;
15038    }
15039    let dependent = parameter
15040        .child_by_field_name("type")
15041        .filter(|type_node| type_node.kind() == "dependent_type")?;
15042    let mut cursor = dependent.walk();
15043    let mut named = dependent
15044        .named_children(&mut cursor)
15045        .filter(|child| !child.is_extra());
15046    match (named.next(), named.next()) {
15047        (Some(name), None) if name.kind() == "type_identifier" => Some(name),
15048        _ => None,
15049    }
15050}
15051
15052#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
15053pub struct CppTemplateParameterNameMatches {
15054    pub any_parameter: bool,
15055    pub type_parameter: bool,
15056}
15057
15058pub fn cpp_template_parameter_name_matches(
15059    parameters: Node<'_>,
15060    name: &str,
15061    mut parameter_name: impl FnMut(Node<'_>) -> Option<String>,
15062) -> CppTemplateParameterNameMatches {
15063    let mut matches = CppTemplateParameterNameMatches::default();
15064    let mut cursor = parameters.walk();
15065    for parameter in parameters.named_children(&mut cursor) {
15066        if parameter_name(parameter).as_deref() == Some(name) {
15067            matches.any_parameter = true;
15068            matches.type_parameter |=
15069                cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type;
15070        }
15071    }
15072    matches
15073}
15074
15075pub fn cpp_type_field_contains_node(parent: Node<'_>, node: Node<'_>) -> bool {
15076    parent.child_by_field_name("type").is_some_and(|type_node| {
15077        type_node.start_byte() <= node.start_byte() && node.end_byte() <= type_node.end_byte()
15078    })
15079}
15080
15081fn cpp_template_parameter_default(node: Node<'_>) -> Option<Node<'_>> {
15082    let node = cpp_template_parameter_declaration(node);
15083    node.child_by_field_name("default_type")
15084        .or_else(|| node.child_by_field_name("default_value"))
15085}
15086
15087fn cpp_template_parameter_default_expression<'tree>(
15088    parameter: Node<'tree>,
15089    source: &str,
15090    parameter_names: &[String],
15091    ancestry: &ParentIndex<'tree>,
15092) -> Option<CppTemplateExpression> {
15093    let default = cpp_template_parameter_default(parameter)?;
15094    let base = cpp_template_expression(default, source, parameter_names, ancestry);
15095    let Some(pointer_error) = parameter.next_named_sibling() else {
15096        return Some(base);
15097    };
15098    // The grammar's default_type accepts a type specifier, not a complete
15099    // type-id. For `const T*` it calls `const` the type and leaves `T*` in
15100    // the next ERROR. Let the type-id grammar interpret that exact range.
15101    if cpp_template_parameter_kind(parameter) == CppTemplateParameterKind::Type
15102        && pointer_error.is_error()
15103    {
15104        let text = &source[default.start_byte()..pointer_error.end_byte()];
15105        let parsed = crate::graph::syntax::parse_sentinel(
15106            "using __bifrost_default = ",
15107            crate::graph::syntax::SentinelInput::region(text),
15108            ";",
15109        );
15110        let root = parsed.tree.root_node();
15111        if !root.has_error()
15112            && let Some(type_node) =
15113                brokk_bifrost_core::analyzer::tree_walk::single_named_child_matching(
15114                    root,
15115                    |child| !child.is_extra(),
15116                )
15117                .filter(|alias| alias.kind() == "alias_declaration")
15118                .and_then(|alias| alias.child_by_field_name("type"))
15119        {
15120            let alias_ancestry = ParentIndex::new(root);
15121            return Some(cpp_template_expression(
15122                type_node,
15123                &parsed.source,
15124                parameter_names,
15125                &alias_ancestry,
15126            ));
15127        }
15128    }
15129    let Some(pointer_declarator) =
15130        recovered_abstract_pointer_declarator_term(pointer_error, source)
15131    else {
15132        return Some(base);
15133    };
15134    Some(CppTemplateExpression {
15135        text: format!(
15136            "{}{}",
15137            base.text,
15138            normalize_cpp_whitespace(node_text(pointer_error, source))
15139        ),
15140        term: CppTemplateTerm::Node {
15141            kind: "type_descriptor".to_string(),
15142            children: vec![base.term, pointer_declarator],
15143        },
15144    })
15145}
15146
15147fn recovered_abstract_pointer_declarator_term(
15148    node: Node<'_>,
15149    source: &str,
15150) -> Option<CppTemplateTerm> {
15151    if node.kind() != "ERROR" || node.child_count() == 0 {
15152        return None;
15153    }
15154    let mut children = Vec::new();
15155    for index in 0..node.child_count() {
15156        let child = node.child(index)?;
15157        if child.kind() != "*" {
15158            return None;
15159        }
15160        children.push(CppTemplateTerm::Atom {
15161            kind: "*".to_string(),
15162            text: normalize_cpp_whitespace(node_text(child, source)),
15163        });
15164    }
15165    Some(CppTemplateTerm::Node {
15166        kind: "abstract_pointer_declarator".to_string(),
15167        children,
15168    })
15169}
15170
15171fn cpp_template_expression<'tree>(
15172    node: Node<'tree>,
15173    source: &str,
15174    parameter_names: &[String],
15175    ancestry: &ParentIndex<'tree>,
15176) -> CppTemplateExpression {
15177    let text = normalize_cpp_whitespace(node_text(node, source));
15178    CppTemplateExpression {
15179        text,
15180        term: cpp_template_term(node, source, parameter_names, ancestry),
15181    }
15182}
15183
15184pub fn cpp_template_term<'tree>(
15185    node: Node<'tree>,
15186    source: &str,
15187    parameter_names: &[String],
15188    ancestry: &ParentIndex<'tree>,
15189) -> CppTemplateTerm {
15190    enum Work<'tree> {
15191        Visit(Node<'tree>),
15192        Build { kind: String, child_count: usize },
15193    }
15194
15195    let mut work = vec![Work::Visit(node)];
15196    let mut terms = Vec::new();
15197    while let Some(next) = work.pop() {
15198        match next {
15199            Work::Visit(current) => {
15200                let text = normalize_cpp_whitespace(node_text(current, source));
15201                if cpp_template_term_leaf_is_parameter(current, &text, parameter_names, ancestry) {
15202                    terms.push(CppTemplateTerm::Parameter(text));
15203                    continue;
15204                }
15205                if matches!(current.kind(), "type_descriptor" | "dependent_type") {
15206                    let mut cursor = current.walk();
15207                    let named = current
15208                        .named_children(&mut cursor)
15209                        .filter(|child| !child.is_extra() && child.kind() != "comment")
15210                        .collect::<Vec<_>>();
15211                    if let [child] = named.as_slice() {
15212                        work.push(Work::Visit(*child));
15213                        continue;
15214                    }
15215                }
15216                if current.child_count() == 0 {
15217                    terms.push(CppTemplateTerm::Atom {
15218                        kind: if matches!(
15219                            current.kind(),
15220                            "identifier"
15221                                | "type_identifier"
15222                                | "field_identifier"
15223                                | "namespace_identifier"
15224                        ) {
15225                            "identifier".to_string()
15226                        } else {
15227                            current.kind().to_string()
15228                        },
15229                        text,
15230                    });
15231                    continue;
15232                }
15233                let children = (0..current.child_count())
15234                    .filter_map(|index| current.child(index))
15235                    .filter(|child| !child.is_extra() && child.kind() != "comment")
15236                    .collect::<Vec<_>>();
15237                work.push(Work::Build {
15238                    kind: current.kind().to_string(),
15239                    child_count: children.len(),
15240                });
15241                work.extend(children.into_iter().rev().map(Work::Visit));
15242            }
15243            Work::Build { kind, child_count } => {
15244                let children = terms.split_off(terms.len() - child_count);
15245                terms.push(CppTemplateTerm::Node { kind, children });
15246            }
15247        }
15248    }
15249    terms.pop().expect("template term traversal emits one root")
15250}
15251
15252fn cpp_template_term_leaf_is_parameter<'tree>(
15253    node: Node<'tree>,
15254    text: &str,
15255    parameter_names: &[String],
15256    ancestry: &ParentIndex<'tree>,
15257) -> bool {
15258    if !parameter_names.iter().any(|parameter| parameter == text) {
15259        return false;
15260    }
15261    !ancestry.parent(node).is_some_and(|parent| {
15262        matches!(
15263            parent.kind(),
15264            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
15265        ) && parent.child_by_field_name("scope").is_some()
15266            && parent.child_by_field_name("name") == Some(node)
15267    })
15268}
15269
15270fn enclosing_cpp_declaration_node<'tree>(
15271    mut node: Node<'tree>,
15272    ancestry: &ParentIndex<'tree>,
15273) -> Option<Node<'tree>> {
15274    loop {
15275        match node.kind() {
15276            "declaration"
15277            | "function_declaration"
15278            | "field_declaration"
15279            | "function_definition" => return Some(node),
15280            _ => node = ancestry.parent(node)?,
15281        }
15282    }
15283}
15284
15285fn cpp_parameter_signature(parameters_node: Node<'_>, source: &str) -> String {
15286    let mut params = Vec::new();
15287    let mut cursor = parameters_node.walk();
15288    for child in parameters_node.children(&mut cursor) {
15289        match child.kind() {
15290            "explicit_object_parameter_declaration" => {
15291                if let Some(parameter) = child
15292                    .named_children(&mut child.walk())
15293                    .find(|node| node.kind() == "parameter_declaration")
15294                {
15295                    params.push(format!("this {}", cpp_parameter_type(parameter, source)));
15296                }
15297            }
15298            "parameter_declaration" | "optional_parameter_declaration" => {
15299                params.push(cpp_parameter_type(child, source));
15300            }
15301            "variadic_parameter_declaration" => {
15302                params.push(cpp_parameter_type(child, source));
15303            }
15304            "variadic_parameter" | "..." => params.push("...".to_string()),
15305            _ => {}
15306        }
15307    }
15308
15309    if params.is_empty() {
15310        "()".to_string()
15311    } else {
15312        format!("({})", params.join(", "))
15313    }
15314}
15315
15316/// Where a C or C++ callable declaration is written, which is what decides
15317/// which modifier facts its own node is able to state.
15318enum CppCallableScope<'tree> {
15319    /// Written directly in a class, struct or union body. The declaration
15320    /// states its own storage class, and its access is the one the body's
15321    /// specifier ladder is under at that point.
15322    ClassBody {
15323        body: Node<'tree>,
15324        default_visibility: DeclaredVisibility,
15325    },
15326    /// A qualified declaration written outside every class body
15327    /// (`void Client::send(int) {}`). C++ forbids repeating `static` here and
15328    /// writes no access specifier, and whether the qualifier names a class or
15329    /// a namespace is not decidable from this file alone -- that is exactly
15330    /// what [`crate::reconcile`] needs the include-visible class table for.
15331    Qualified,
15332    /// Namespace scope or file scope, reached by an unqualified name. C has no
15333    /// member functions at all, so every C callable lands here.
15334    Unqualified,
15335}
15336
15337/// Where the declaration sits, from one upward walk of its own ancestry.
15338///
15339/// The walk stops as soon as something proves the declaration is outside every
15340/// class body: a `namespace_definition`, because a class body never encloses
15341/// one, and a `compound_statement`, because a body opens a block scope and
15342/// nothing declared in it is a member of the class the enclosing definition
15343/// belongs to. That is the same rule
15344/// `CppVisitor::visit_local_types_in_function` applies when it
15345/// drops the aggregate carrier, and it keeps this question off the O(depth)
15346/// walk a deeply nested generated header would otherwise pay per callable.
15347///
15348/// `function_declarator` is `None` for a declaration whose callable the macro
15349/// recovery reparsed out of a malformed member: there is no declarator node to
15350/// ask whether the name carries a qualifier, and a recovered member is written
15351/// in its class body anyway.
15352fn cpp_callable_scope<'tree>(
15353    declaration: Node<'tree>,
15354    function_declarator: Option<Node<'tree>>,
15355    ancestry: &ParentIndex<'tree>,
15356) -> CppCallableScope<'tree> {
15357    let mut current = Some(declaration);
15358    while let Some(node) = current {
15359        match node.kind() {
15360            "field_declaration_list" => {
15361                let default_visibility =
15362                    ancestry
15363                        .parent(node)
15364                        .map_or(DeclaredVisibility::Unknown, |class_like| {
15365                            // `struct` and `union` members default to public;
15366                            // `class` members default to private.
15367                            match class_like.kind() {
15368                                "struct_specifier" | "union_specifier" => {
15369                                    DeclaredVisibility::Public
15370                                }
15371                                _ => DeclaredVisibility::Private,
15372                            }
15373                        });
15374                return CppCallableScope::ClassBody {
15375                    body: node,
15376                    default_visibility,
15377                };
15378            }
15379            "namespace_definition" | "compound_statement" | "translation_unit" => break,
15380            _ => {}
15381        }
15382        current = ancestry.parent(node);
15383    }
15384    if cpp_callable_declarator_is_qualified(function_declarator) {
15385        CppCallableScope::Qualified
15386    } else {
15387        CppCallableScope::Unqualified
15388    }
15389}
15390
15391/// Whether the declarator's own written name carries a `::` qualifier.
15392///
15393/// A qualified declaration written outside every class body
15394/// (`long Client::send(int) {}`) names a member whose class body this file may
15395/// not contain, which is what [`cpp_callable_scope`] calls `Qualified` and the
15396/// reason [`cpp_callable_dispatch_extensibility`] refuses to answer for one.
15397fn cpp_callable_declarator_is_qualified(function_declarator: Option<Node<'_>>) -> bool {
15398    function_declarator
15399        .and_then(cpp_function_declarator_name_node)
15400        .is_some_and(|name| name.kind() == "qualified_identifier")
15401}
15402
15403/// The access one class-body member declaration is written under.
15404///
15405/// C++ states member access positionally: an `access_specifier` applies to
15406/// every member written after it until the next one, and the members before
15407/// the first one take the class keyword's default. The answer is therefore the
15408/// last specifier the body writes before the member's own list-level node,
15409/// read from the specifier's own keyword token rather than from its text. A
15410/// label a macro invocation swallowed has no specifier node or keyword token;
15411/// [`cpp_macro_swallowed_access_label`] reads it from the declarator the
15412/// grammar made of the keyword.
15413///
15414/// The body's children are read forward with one cursor rather than backwards
15415/// from the member: `Node::prev_sibling` re-descends from the root for each
15416/// step, which is the per-edge cost #2361 keeps off this walk.
15417fn cpp_member_declared_visibility<'tree>(
15418    declaration: Node<'tree>,
15419    body: Node<'tree>,
15420    default_visibility: DeclaredVisibility,
15421    source: &str,
15422    ancestry: &ParentIndex<'tree>,
15423) -> DeclaredVisibility {
15424    let mut list_level = declaration;
15425    while ancestry.parent(list_level) != Some(body) {
15426        let Some(parent) = ancestry.parent(list_level) else {
15427            return default_visibility;
15428        };
15429        list_level = parent;
15430    }
15431    let mut visibility = default_visibility;
15432    let mut cursor = body.walk();
15433    for child in body.children(&mut cursor) {
15434        if child == list_level {
15435            break;
15436        }
15437        let access = if child.kind() == "access_specifier" {
15438            child.child(0).map(|keyword| keyword.kind())
15439        } else {
15440            cpp_macro_swallowed_access_label(child, source).map(|label| label.access)
15441        };
15442        visibility = match access {
15443            Some("public") => DeclaredVisibility::Public,
15444            Some("protected") => DeclaredVisibility::Protected,
15445            Some("private") => DeclaredVisibility::Private,
15446            _ => visibility,
15447        };
15448    }
15449    visibility
15450}
15451
15452/// Whether this declaration writes the `static` storage class specifier.
15453///
15454/// What `static` *means* is the scope's business: on a class-body member it is
15455/// the receiver fact, and at file scope it is internal linkage, which is why
15456/// both [`cpp_callable_modifiers`] and [`cpp_callable_linkage`] ask the same
15457/// question and answer it differently.
15458fn cpp_declares_static_storage_class(declaration: Node<'_>, source: &str) -> bool {
15459    let mut cursor = declaration.walk();
15460    declaration.named_children(&mut cursor).any(|child| {
15461        child.kind() == "storage_class_specifier"
15462            && normalize_cpp_whitespace(node_text(child, source)) == "static"
15463    })
15464}
15465
15466/// The three modifier facts [`SignatureMetadata::with_callable_modifiers`]
15467/// publishes, as C and C++ state them.
15468struct CppCallableModifiers {
15469    is_static: bool,
15470    is_constructor: bool,
15471    visibility: DeclaredVisibility,
15472}
15473
15474/// The modifier facts a C or C++ callable declaration states in its own shape.
15475///
15476/// C++ has no `static` keyword for receivers and no visibility keyword of the
15477/// Java kind: it states both in the declaration's position. A member declared
15478/// in a class body binds the object it is selected on unless that declaration
15479/// writes the `static` storage class, and its access is the one the body's
15480/// specifier ladder is under. A function at namespace or file scope binds
15481/// nothing and has no access specifier at all, so its declared visibility is
15482/// Unknown. Linkage is recorded separately and does not declare access. C has
15483/// no member functions, and the `static` a C function writes is internal linkage,
15484/// never a receiver fact.
15485///
15486/// Recording these is what lets `receiver_contract_of` answer for a C or C++
15487/// declaration. Without them it reported no contract at all,
15488/// `modeled_procedure_key_for_unit` refused to key any C or C++ workspace
15489/// callable, no reviewed summary could name one, and every effect or taint walk
15490/// that reached one reported `callee_unkeyable` (#3493). Go closed the same gap
15491/// in #3455, Kotlin in #3453, Python in #3451.
15492///
15493/// An out-of-line member definition cannot repeat `static` or access. These
15494/// are per-file facts only: the analyzer reconciles the exact overload with
15495/// include-visible class declarations before exposing its receiver contract
15496/// (#3509). A missing declaration leaves that contract unknown.
15497fn cpp_callable_modifiers<'tree>(
15498    declaration: Node<'tree>,
15499    function_declarator: Option<Node<'tree>>,
15500    is_constructor: bool,
15501    source: &str,
15502    ancestry: &ParentIndex<'tree>,
15503) -> CppCallableModifiers {
15504    match cpp_callable_scope(declaration, function_declarator, ancestry) {
15505        CppCallableScope::ClassBody {
15506            body,
15507            default_visibility,
15508        } => CppCallableModifiers {
15509            is_static: cpp_declares_static_storage_class(declaration, source),
15510            is_constructor,
15511            visibility: cpp_member_declared_visibility(
15512                declaration,
15513                body,
15514                default_visibility,
15515                source,
15516                ancestry,
15517            ),
15518        },
15519        CppCallableScope::Qualified => CppCallableModifiers {
15520            is_static: false,
15521            is_constructor,
15522            visibility: DeclaredVisibility::Unknown,
15523        },
15524        CppCallableScope::Unqualified => CppCallableModifiers {
15525            is_static: false,
15526            is_constructor,
15527            visibility: DeclaredVisibility::Unknown,
15528        },
15529    }
15530}
15531
15532/// The persisted signature facts one C or C++ callable declaration publishes.
15533///
15534/// `linkage` is the caller's, not this function's: the node that carries the
15535/// storage class is not always the declaration this declarator sits in -- a
15536/// recovered constructor reads it from its class declaration, and a
15537/// macro-wrapped declaration reads the `static` its recovery recorded -- so the
15538/// visit site that knows which node states it passes the answer in. Recording
15539/// it here keeps it beside the declared visibility it also decides.
15540fn cpp_signature_metadata<'tree>(
15541    signature: String,
15542    function_declarator: Node<'tree>,
15543    linkage: CallableLinkage,
15544    source: &str,
15545    ancestry: &ParentIndex<'tree>,
15546) -> SignatureMetadata {
15547    let dispatch = cpp_callable_dispatch_extensibility(function_declarator, ancestry);
15548    let is_constructor =
15549        cpp_callable_is_structural_constructor(function_declarator, source, ancestry);
15550    let modifiers = match enclosing_cpp_declaration_node(function_declarator, ancestry) {
15551        Some(declaration) => cpp_callable_modifiers(
15552            declaration,
15553            Some(function_declarator),
15554            is_constructor,
15555            source,
15556            ancestry,
15557        ),
15558        // Severe recovery left no declaration node above this declarator, so
15559        // nothing is stated here beyond what the callable's own name proves.
15560        None => CppCallableModifiers {
15561            is_static: false,
15562            is_constructor,
15563            visibility: DeclaredVisibility::Unknown,
15564        },
15565    };
15566    let enrich = |mut metadata: SignatureMetadata| {
15567        metadata = metadata
15568            .with_overload_discriminator(cpp_overload_discriminator(function_declarator, source));
15569        // #3507: a qualified definition repeats none of the class body's
15570        // dispatch facts, so this row publishes none either.
15571        if let Some(dispatch) = dispatch {
15572            metadata = metadata.with_dispatch_extensibility(dispatch);
15573        }
15574        metadata
15575            .with_callable_linkage(linkage)
15576            .with_callable_modifiers(
15577                modifiers.is_static,
15578                modifiers.is_constructor,
15579                modifiers.visibility,
15580            )
15581    };
15582    let return_type_text = cpp_callable_return_type_text(function_declarator, source, ancestry);
15583    let return_type_identity =
15584        cpp_callable_return_type_identity(function_declarator, source, ancestry);
15585    let Some(parameters_node) = function_declarator.child_by_field_name("parameters") else {
15586        return enrich(
15587            SignatureMetadata::new(signature, Vec::new())
15588                .with_return_type_text(return_type_text)
15589                .with_return_type_identity(return_type_identity),
15590        );
15591    };
15592    let callable_slots = cpp_callable_parameter_slots(parameters_node, source).ok();
15593    let callable_arity = callable_slots.as_deref().map(cpp_callable_arity_from_slots);
15594    let callable_parameter_types = callable_slots
15595        .as_deref()
15596        .map(|slots| cpp_callable_parameter_types_from_slots(slots, source));
15597    let with_parameter_facts = |mut metadata: SignatureMetadata| {
15598        if let Some(arity) = callable_arity {
15599            metadata = metadata.with_callable_arity(arity);
15600        }
15601        if let Some(types) = &callable_parameter_types {
15602            metadata = metadata.with_callable_parameter_types(types.clone());
15603        }
15604        metadata
15605    };
15606    let parameter_text = normalize_cpp_whitespace(node_text(parameters_node, source));
15607    let search_from = cpp_signature_search_start(&signature, function_declarator, source, ancestry);
15608    let Some(relative_start) = signature
15609        .get(search_from..)
15610        .and_then(|suffix| suffix.find(&parameter_text))
15611    else {
15612        return enrich(with_parameter_facts(
15613            SignatureMetadata::new(signature, Vec::new())
15614                .with_return_type_text(return_type_text)
15615                .with_return_type_identity(return_type_identity),
15616        ));
15617    };
15618    let parameters_start = search_from + relative_start;
15619    let parameters_end = parameters_start + parameter_text.len();
15620    let mut search_start = parameters_start;
15621    let parameters = cpp_parameter_label_nodes(parameters_node)
15622        .into_iter()
15623        .filter_map(|label_node| {
15624            let label = normalize_cpp_whitespace(node_text(label_node, source));
15625            if label.is_empty() || search_start > parameters_end {
15626                return None;
15627            }
15628            let haystack = signature.get(search_start..parameters_end)?;
15629            let relative_start = haystack.find(&label)?;
15630            let start_byte = search_start + relative_start;
15631            let end_byte = start_byte + label.len();
15632            search_start = end_byte;
15633            Some(ParameterMetadata::new(label, start_byte, end_byte))
15634        })
15635        .collect();
15636    enrich(with_parameter_facts(
15637        SignatureMetadata::new(signature, parameters)
15638            .with_return_type_text(return_type_text)
15639            .with_return_type_identity(return_type_identity),
15640    ))
15641}
15642
15643pub(crate) fn cpp_callable_is_structural_constructor<'tree>(
15644    function_declarator: Node<'tree>,
15645    source: &str,
15646    ancestry: &ParentIndex<'tree>,
15647) -> bool {
15648    let Some(name_node) = cpp_function_declarator_name_node(function_declarator)
15649        .or_else(|| function_declarator.child_by_field_name("name"))
15650        .or_else(|| last_named_child(function_declarator))
15651    else {
15652        return false;
15653    };
15654    // An out-of-line definition writes the class in the name itself
15655    // (`Client::Client()`, `shop::Client::Client()`), so the qualifier's own
15656    // terminal component answers without any enclosing class body.
15657    if name_node.kind() == "qualified_identifier" {
15658        let owner = name_node
15659            .child_by_field_name("scope")
15660            .and_then(|scope| canonical_cpp_qualified_component(scope, source));
15661        let member = name_node
15662            .child_by_field_name("name")
15663            .and_then(|name| canonical_cpp_qualified_component(name, source));
15664        return match (owner, member) {
15665            (Some(owner), Some(member)) => owner.name == member.name,
15666            _ => false,
15667        };
15668    }
15669    let Some(callable_name) = direct_identifier_name(name_node, source) else {
15670        return false;
15671    };
15672
15673    let mut current = ancestry.parent(function_declarator);
15674    while let Some(ancestor) = current {
15675        let owner_name = match ancestor.kind() {
15676            "class_specifier" | "struct_specifier" | "union_specifier" => {
15677                class_like_name(ancestor, source, ancestry)
15678            }
15679            "ERROR" => malformed_class_error_owner_name(ancestor, source),
15680            _ => None,
15681        };
15682        if owner_name.is_some_and(|owner_name| owner_name == callable_name) {
15683            return true;
15684        }
15685        current = ancestry.parent(ancestor);
15686    }
15687    false
15688}
15689
15690/// Recover the owner name from the direct grammar shape retained when a later
15691/// member macro makes tree-sitter reduce an otherwise ordinary class body to an
15692/// `ERROR` node:
15693///
15694/// `ERROR(class, type_identifier, base_class_clause?, "{", members...)`
15695///
15696/// Direct-child checks keep this distinct from an unrelated nested class inside
15697/// a broader error region. The closing brace may be displaced past the error
15698/// node, so the opening body token is the available structural boundary.
15699fn malformed_class_error_owner_name(node: Node<'_>, source: &str) -> Option<String> {
15700    if node.kind() != "ERROR" {
15701        return None;
15702    }
15703    let keyword = node.child(0)?;
15704    if !matches!(keyword.kind(), "class" | "struct" | "union") {
15705        return None;
15706    }
15707    let name_node = node.child(1)?;
15708    let name = direct_identifier_name(name_node, source)?;
15709    let has_body = (2..node.child_count())
15710        .filter_map(|index| node.child(index))
15711        .any(|child| child.kind() == "{");
15712    has_body.then_some(name)
15713}
15714
15715/// Whether one declaration carries a native must-use attribute.
15716///
15717/// Attribute names are read only from structurally selected attribute nodes;
15718/// an identifier with the same spelling elsewhere in the declaration cannot
15719/// produce a proof. GNU's spelling is accepted for `warn_unused_result`,
15720/// while standard `nodiscard` is accepted without a namespace prefix.
15721pub fn cpp_declaration_has_native_must_use_attribute(declaration: Node<'_>, source: &str) -> bool {
15722    // The C++ grammar keeps a prefix attribute on the enclosing declaration
15723    // while the enum unit's occurrence points at its `enum_specifier`. Bring
15724    // that one structural sibling into the same declaration boundary; this
15725    // does not widen the walk to surrounding declarations.
15726    let declaration = declaration
15727        .parent()
15728        .filter(|parent| {
15729            parent.kind() == "declaration"
15730                && parent
15731                    .child_by_field_name("type")
15732                    .is_some_and(|type_node| type_node.id() == declaration.id())
15733        })
15734        .unwrap_or(declaration);
15735    let mut pending = vec![declaration];
15736    while let Some(node) = pending.pop() {
15737        if cpp_attribute_node_is_native_must_use(node, source)
15738            && cpp_attribute_is_attached_to_declaration(node, declaration)
15739        {
15740            return true;
15741        }
15742        if node != declaration && matches!(node.kind(), "compound_statement" | "lambda_expression")
15743        {
15744            continue;
15745        }
15746        push_named_children_reversed(node, &mut pending);
15747    }
15748    false
15749}
15750
15751fn cpp_attribute_node_is_native_must_use(node: Node<'_>, source: &str) -> bool {
15752    if node.kind() == "attribute" {
15753        return node.child_by_field_name("name").is_some_and(|name| {
15754            let name = node_text(name, source);
15755            let prefix = node
15756                .child_by_field_name("prefix")
15757                .map(|prefix| node_text(prefix, source));
15758            matches!(
15759                (prefix, name),
15760                (None, "nodiscard")
15761                    | (None, "warn_unused_result")
15762                    | (Some("gnu"), "warn_unused_result")
15763            )
15764        });
15765    }
15766    if node.kind() != "attribute_specifier" || !node_text(node, source).starts_with("__attribute__")
15767    {
15768        return false;
15769    }
15770    let Some(arguments) = node.named_child(0) else {
15771        return false;
15772    };
15773    arguments.kind() == "argument_list"
15774        && arguments.named_children(&mut arguments.walk()).any(|name| {
15775            name.kind() == "identifier" && node_text(name, source) == "warn_unused_result"
15776        })
15777}
15778
15779fn cpp_attribute_is_attached_to_declaration(attribute: Node<'_>, declaration: Node<'_>) -> bool {
15780    let mut current = attribute;
15781    while let Some(parent) = current.parent() {
15782        if parent.id() == declaration.id() {
15783            return true;
15784        }
15785        if matches!(
15786            parent.kind(),
15787            "declaration"
15788                | "field_declaration"
15789                | "function_definition"
15790                | "struct_specifier"
15791                | "class_specifier"
15792                | "union_specifier"
15793                | "enum_specifier"
15794        ) {
15795            return false;
15796        }
15797        current = parent;
15798    }
15799    false
15800}
15801
15802/// Whether export/decorator macros ahead of a callable declaration's `type`
15803/// make that type head decoration rather than a semantic return type. The
15804/// older grammar leaves the macro token in the `type` field and the real type
15805/// in an ERROR sibling; the macro-declaration grammar wraps the same token as a
15806/// leading `macro_modifier`, or the whole invocation as a
15807/// `macro_type_specifier` type. Such a declaration does not carry enough
15808/// structured evidence to persist a return type, as text or as identity.
15809fn cpp_declaration_return_type_is_macro_decorated(
15810    declaration: Node<'_>,
15811    type_node: Node<'_>,
15812    source: &str,
15813) -> bool {
15814    type_node.kind() == "macro_type_specifier"
15815        || cpp_declaration_type_follows_macro_modifier(declaration, type_node)
15816        || (cpp_export_macro_token(node_text(type_node, source))
15817            && (0..declaration.named_child_count()).any(|index| {
15818                declaration
15819                    .named_child(index)
15820                    .is_some_and(|child| child.kind() == "ERROR")
15821            }))
15822}
15823
15824/// Whether a `macro_modifier` precedes `type_node` among `declaration`'s
15825/// specifiers: the macro-declaration grammar's clean reading of an unexpanded
15826/// decorator macro ahead of the declared type (`MACRO static inline T f()`).
15827pub(crate) fn cpp_declaration_type_follows_macro_modifier(
15828    declaration: Node<'_>,
15829    type_node: Node<'_>,
15830) -> bool {
15831    let mut head_cursor = declaration.walk();
15832    declaration
15833        .named_children(&mut head_cursor)
15834        .take_while(|child| *child != type_node)
15835        .any(|child| child.kind() == "macro_modifier")
15836}
15837
15838fn cpp_callable_return_type_parts<'tree>(
15839    function_declarator: Node<'tree>,
15840    source: &str,
15841    ancestry: &ParentIndex<'tree>,
15842) -> Option<(Node<'tree>, Vec<CppStructuredTypeWrapper>)> {
15843    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
15844        return None;
15845    }
15846    if let Some((return_type, _)) =
15847        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
15848    {
15849        return Some((return_type, Vec::new()));
15850    }
15851    let mut cursor = function_declarator.walk();
15852    if let Some(trailing) = function_declarator
15853        .named_children(&mut cursor)
15854        .find(|child| child.kind() == "trailing_return_type")
15855        && let Some(type_descriptor) = trailing.named_child(0)
15856    {
15857        return Some((type_descriptor, Vec::new()));
15858    }
15859
15860    let mut current = function_declarator;
15861    let mut wrappers = Vec::new();
15862    while let Some(parent) = ancestry.parent(current) {
15863        if matches!(
15864            parent.kind(),
15865            "function_definition" | "declaration" | "field_declaration"
15866        ) {
15867            let type_node = parent.child_by_field_name("type")?;
15868            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
15869                return None;
15870            }
15871            return Some((type_node, wrappers));
15872        }
15873        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
15874            || (matches!(
15875                parent.kind(),
15876                "pointer_declarator"
15877                    | "reference_declarator"
15878                    | "array_declarator"
15879                    | "parenthesized_declarator"
15880            ) && parent.named_child_count() == 1
15881                && parent.named_child(0) == Some(current));
15882        if !wraps_current_declarator {
15883            return None;
15884        }
15885        match parent.kind() {
15886            "pointer_declarator" => wrappers.push(CppStructuredTypeWrapper::Pointer),
15887            "reference_declarator" => wrappers.push(cpp_reference_wrapper(parent)?),
15888            "array_declarator" => wrappers.push(CppStructuredTypeWrapper::Array),
15889            "init_declarator" | "parenthesized_declarator" | "attributed_declarator" => {}
15890            _ => return None,
15891        }
15892        current = parent;
15893    }
15894    None
15895}
15896
15897/// Return the AST node that describes one callable's declared result type.
15898///
15899/// The node is returned only after the same declarator walk that establishes
15900/// the persisted C++ return identity. Consumers can therefore resolve a
15901/// nominal result or inspect its declaration attributes without parsing a
15902/// rendered signature.
15903pub fn cpp_callable_declaration_return_type_node<'tree>(
15904    callable: Node<'tree>,
15905    source: &str,
15906    ancestry: &ParentIndex<'tree>,
15907) -> Option<Node<'tree>> {
15908    let declarator = callable
15909        .child_by_field_name("declarator")
15910        .and_then(extract_function_declarator)?;
15911    cpp_callable_return_type_parts(declarator, source, ancestry).map(|(node, _)| node)
15912}
15913
15914/// Return the structured declared result type of one callable declaration.
15915///
15916/// This accepts the enclosing declaration node so consumers do not need to
15917/// duplicate the declarator-unwrapping rules before asking for the structured
15918/// identity.
15919pub fn cpp_callable_declaration_return_type_identity<'tree>(
15920    callable: Node<'tree>,
15921    source: &str,
15922    ancestry: &ParentIndex<'tree>,
15923) -> Option<StructuredTypeIdentity> {
15924    let declarator = callable
15925        .child_by_field_name("declarator")
15926        .and_then(extract_function_declarator)?;
15927    cpp_callable_return_type_identity(declarator, source, ancestry)
15928}
15929
15930pub(crate) fn cpp_callable_return_type_identity<'tree>(
15931    function_declarator: Node<'tree>,
15932    source: &str,
15933    ancestry: &ParentIndex<'tree>,
15934) -> Option<StructuredTypeIdentity> {
15935    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
15936    let (return_type, wrappers) =
15937        cpp_callable_return_type_parts(function_declarator, source, ancestry)?;
15938    cpp_apply_structured_type_wrappers(
15939        cpp_structured_type_identity(return_type, source, &lexical_scope)?,
15940        wrappers,
15941    )
15942}
15943
15944fn cpp_structured_type_identity(
15945    node: Node<'_>,
15946    source: &str,
15947    lexical_scope: &[String],
15948) -> Option<StructuredTypeIdentity> {
15949    enum Work<'tree> {
15950        Visit(Node<'tree>),
15951        Wrap(CppStructuredTypeWrapper),
15952        ApplyWrappers(Vec<CppStructuredTypeWrapper>),
15953        BuildGeneric { argument_count: usize },
15954    }
15955
15956    let mut work = vec![Work::Visit(node)];
15957    let mut values = Vec::new();
15958    let mut builder = StructuredTypeIdentityBuilder::default();
15959    while let Some(next) = work.pop() {
15960        match next {
15961            Work::Visit(current) => match current.kind() {
15962                "type_descriptor" => {
15963                    let type_node = current
15964                        .child_by_field_name("type")
15965                        .or_else(|| current.named_child(0))?;
15966                    let mut wrappers = Vec::new();
15967                    let mut cursor = current.walk();
15968                    for child in current.named_children(&mut cursor) {
15969                        if child.id() != type_node.id() {
15970                            wrappers.extend(cpp_structured_declarator_wrappers(child)?);
15971                        }
15972                    }
15973                    work.push(Work::ApplyWrappers(wrappers));
15974                    work.push(Work::Visit(type_node));
15975                }
15976                "pointer_declarator" | "abstract_pointer_declarator" => {
15977                    let child = current
15978                        .child_by_field_name("declarator")
15979                        .or_else(|| current.named_child(0))?;
15980                    work.push(Work::Wrap(CppStructuredTypeWrapper::Pointer));
15981                    work.push(Work::Visit(child));
15982                }
15983                "reference_declarator" => {
15984                    let child = current
15985                        .child_by_field_name("declarator")
15986                        .or_else(|| current.named_child(0))?;
15987                    work.push(Work::Wrap(cpp_reference_wrapper(current)?));
15988                    work.push(Work::Visit(child));
15989                }
15990                "array_declarator" | "abstract_array_declarator" => {
15991                    let child = current
15992                        .child_by_field_name("declarator")
15993                        .or_else(|| current.named_child(0))?;
15994                    work.push(Work::Wrap(CppStructuredTypeWrapper::Array));
15995                    work.push(Work::Visit(child));
15996                }
15997                "template_type" => {
15998                    let name_node = current.child_by_field_name("name")?;
15999                    let arguments = current
16000                        .child_by_field_name("arguments")
16001                        .map(|arguments_node| {
16002                            let mut cursor = arguments_node.walk();
16003                            arguments_node
16004                                .named_children(&mut cursor)
16005                                .filter(|child| !child.is_extra() && child.kind() != "comment")
16006                                .collect::<Vec<_>>()
16007                        })
16008                        .unwrap_or_default();
16009                    work.push(Work::BuildGeneric {
16010                        argument_count: arguments.len(),
16011                    });
16012                    work.extend(arguments.into_iter().rev().map(Work::Visit));
16013                    work.push(Work::Visit(name_node));
16014                }
16015                "qualified_identifier"
16016                | "scoped_identifier"
16017                | "scoped_type_identifier"
16018                | "type_identifier"
16019                | "field_identifier"
16020                | "identifier"
16021                | "namespace_identifier"
16022                | "primitive_type" => {
16023                    values.push(builder.named(cpp_structured_named_type(
16024                        current,
16025                        source,
16026                        lexical_scope,
16027                    )?)?);
16028                }
16029                _ => {
16030                    let child = current.child_by_field_name("type").or_else(|| {
16031                        (current.named_child_count() == 1)
16032                            .then(|| current.named_child(0))
16033                            .flatten()
16034                    })?;
16035                    work.push(Work::Visit(child));
16036                }
16037            },
16038            Work::Wrap(wrapper) => {
16039                let root = values.pop()?;
16040                values.push(cpp_wrap_structured_type_node(&mut builder, root, wrapper)?);
16041            }
16042            Work::ApplyWrappers(wrappers) => {
16043                let mut root = values.pop()?;
16044                for wrapper in wrappers.into_iter().rev() {
16045                    root = cpp_wrap_structured_type_node(&mut builder, root, wrapper)?;
16046                }
16047                values.push(root);
16048            }
16049            Work::BuildGeneric { argument_count } => {
16050                let value_count = argument_count.checked_add(1)?;
16051                let start = values.len().checked_sub(value_count)?;
16052                let mut built = values.split_off(start);
16053                let base = built.remove(0);
16054                values.push(builder.generic(base, built)?);
16055            }
16056        }
16057    }
16058    (values.len() == 1)
16059        .then(|| values.pop())
16060        .flatten()
16061        .and_then(|root| builder.finish(root))
16062}
16063
16064fn cpp_structured_named_type(
16065    node: Node<'_>,
16066    source: &str,
16067    lexical_scope: &[String],
16068) -> Option<StructuredTypeName> {
16069    let path = cpp_structured_type_path(node, source)?;
16070    let absolute = node.child_by_field_name("scope").is_none()
16071        && node.child(0).is_some_and(|child| child.kind() == "::");
16072    StructuredTypeName::new(path, lexical_scope.to_vec(), absolute)
16073}
16074
16075#[derive(Clone, Copy)]
16076enum CppStructuredTypeWrapper {
16077    Pointer,
16078    LvalueReference,
16079    RvalueReference,
16080    Array,
16081}
16082
16083fn cpp_structured_declarator_wrappers(node: Node<'_>) -> Option<Vec<CppStructuredTypeWrapper>> {
16084    let mut wrappers = Vec::new();
16085    let mut current = node;
16086    loop {
16087        match current.kind() {
16088            "pointer_declarator" | "abstract_pointer_declarator" => {
16089                wrappers.push(CppStructuredTypeWrapper::Pointer)
16090            }
16091            "reference_declarator" | "abstract_reference_declarator" => {
16092                wrappers.push(cpp_reference_wrapper(current)?);
16093            }
16094            "array_declarator" | "abstract_array_declarator" => {
16095                wrappers.push(CppStructuredTypeWrapper::Array)
16096            }
16097            // An initializer and a grouping parenthesis are not type
16098            // structure. Stopping at one of them dropped every wrapper an
16099            // initialized or parenthesized declarator writes, so
16100            // `std::string &alias = value;` reported the unqualified
16101            // `std::string` while `const std::string &value` as a parameter
16102            // reported the reference it shares.
16103            "init_declarator" | "parenthesized_declarator" => {}
16104            _ => break,
16105        }
16106        let Some(child) = current
16107            .child_by_field_name("declarator")
16108            .or_else(|| current.named_child(0))
16109        else {
16110            break;
16111        };
16112        current = child;
16113    }
16114    Some(wrappers)
16115}
16116
16117fn cpp_reference_wrapper(node: Node<'_>) -> Option<CppStructuredTypeWrapper> {
16118    node.children(&mut node.walk())
16119        .find_map(|child| match child.kind() {
16120            "&" => Some(CppStructuredTypeWrapper::LvalueReference),
16121            "&&" => Some(CppStructuredTypeWrapper::RvalueReference),
16122            _ => None,
16123        })
16124}
16125
16126fn cpp_wrap_structured_type(
16127    identity: StructuredTypeIdentity,
16128    wrapper: CppStructuredTypeWrapper,
16129) -> Option<StructuredTypeIdentity> {
16130    match wrapper {
16131        CppStructuredTypeWrapper::Pointer => identity.wrap_pointer(),
16132        CppStructuredTypeWrapper::LvalueReference => identity.wrap_reference(),
16133        CppStructuredTypeWrapper::RvalueReference => identity.wrap_rvalue_reference(),
16134        CppStructuredTypeWrapper::Array => identity.wrap_array(),
16135    }
16136}
16137
16138fn cpp_apply_structured_type_wrappers(
16139    mut identity: StructuredTypeIdentity,
16140    wrappers: Vec<CppStructuredTypeWrapper>,
16141) -> Option<StructuredTypeIdentity> {
16142    for wrapper in wrappers.into_iter().rev() {
16143        identity = cpp_wrap_structured_type(identity, wrapper)?;
16144    }
16145    Some(identity)
16146}
16147
16148fn cpp_wrap_structured_type_node(
16149    builder: &mut StructuredTypeIdentityBuilder,
16150    inner: StructuredTypeNodeId,
16151    wrapper: CppStructuredTypeWrapper,
16152) -> Option<StructuredTypeNodeId> {
16153    match wrapper {
16154        CppStructuredTypeWrapper::Pointer => builder.pointer(inner),
16155        CppStructuredTypeWrapper::LvalueReference => builder.reference(inner),
16156        CppStructuredTypeWrapper::RvalueReference => builder.rvalue_reference(inner),
16157        CppStructuredTypeWrapper::Array => builder.array(inner),
16158    }
16159}
16160
16161/// The dot-free name components a qualified C++ name node writes, outermost
16162/// first.
16163///
16164/// The walk reads the grammar's `scope` and `name` fields rather than cutting
16165/// a rendered spelling on `::`, so `std::move` yields `["std", "move"]` and a
16166/// shape the grammar does not name yields nothing at all.
16167pub fn cpp_structured_type_path(node: Node<'_>, source: &str) -> Option<Vec<String>> {
16168    let mut path = Vec::new();
16169    let mut stack = vec![node];
16170    while let Some(current) = stack.pop() {
16171        match current.kind() {
16172            "identifier" | "namespace_identifier" | "type_identifier" | "primitive_type" => {
16173                let component = node_text(current, source).to_string();
16174                if component.is_empty() {
16175                    return None;
16176                }
16177                path.push(component);
16178            }
16179            "template_type" | "dependent_type" => {
16180                stack.push(current.child_by_field_name("name")?);
16181            }
16182            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
16183                stack.push(current.child_by_field_name("name")?);
16184                if let Some(scope) = current.child_by_field_name("scope") {
16185                    stack.push(scope);
16186                }
16187            }
16188            _ => return None,
16189        }
16190    }
16191    (!path.is_empty()).then_some(path)
16192}
16193
16194fn cpp_callable_lexical_scope<'tree>(
16195    node: Node<'tree>,
16196    source: &str,
16197    ancestry: &ParentIndex<'tree>,
16198) -> Vec<String> {
16199    let mut groups = Vec::new();
16200    let mut current = ancestry.parent(node);
16201    while let Some(parent) = current {
16202        if matches!(
16203            parent.kind(),
16204            "namespace_definition" | "class_specifier" | "struct_specifier" | "union_specifier"
16205        ) && let Some(name_node) = parent.child_by_field_name("name")
16206            && let Some(components) = cpp_structured_type_path(name_node, source)
16207            && !components.is_empty()
16208        {
16209            groups.push(components);
16210        }
16211        current = ancestry.parent(parent);
16212    }
16213    groups.reverse();
16214    groups.into_iter().flatten().collect()
16215}
16216
16217/// Whether a call on this callable's static target can select another body.
16218///
16219/// C++ says this where the member is *declared*, not where it is defined, and
16220/// that is the whole difficulty of the question: virtual-ness is inherited
16221/// (`struct Derived : Base { void run(); }` makes `Derived::run` virtual when
16222/// `Base::run` is), an out-of-line definition repeats none of it, and whether
16223/// a definition's qualifier names a class or a namespace is a class-table
16224/// question. So a row answers `Closed` only when its own node proves it:
16225///
16226/// * a written `final` is the one specifier that ends the family here, and it
16227///   is the only thing that closes a member of a class that has a base clause;
16228/// * a class-body member is `Closed` otherwise only when its class body writes
16229///   no base clause at all, because a base clause is the only evidence in this
16230///   file that some base may declare this member virtual;
16231/// * anything with a virtual-family boundary, a template head, or a
16232///   preprocessor or error region is `Open`;
16233/// * a qualified declaration outside every class body answers `None`: its
16234///   class body, which is where the fact is written, may be in another file.
16235///
16236/// The analyzer joins the `None` rows with the include-visible class-body rows
16237/// of the same logical callable (#3507), the same join #3509 makes for a
16238/// definition's declared access; a row that keeps `None` after that join stays
16239/// unknown and no dispatch proof may be built on it.
16240fn cpp_callable_dispatch_extensibility<'tree>(
16241    function_declarator: Node<'tree>,
16242    ancestry: &ParentIndex<'tree>,
16243) -> Option<DispatchExtensibility> {
16244    let mut declaration = None;
16245    // The first class body this declarator is written in. The upward walk
16246    // visits it anyway, so only the one step from the body to its class node
16247    // is extra, and only for a class-body member.
16248    let mut class_body = None;
16249    let mut current = Some(function_declarator);
16250    while let Some(node) = current {
16251        match node.kind() {
16252            "template_declaration"
16253            | "preproc_if"
16254            | "preproc_ifdef"
16255            | "preproc_else"
16256            | "preproc_elif"
16257            | "preproc_call"
16258            | "ERROR" => return Some(DispatchExtensibility::Open),
16259            "declaration" | "field_declaration" | "function_definition" => {
16260                declaration.get_or_insert(node);
16261            }
16262            "field_declaration_list" => {
16263                class_body.get_or_insert(node);
16264            }
16265            "translation_unit" => break,
16266            _ => {}
16267        }
16268        current = ancestry.parent(node);
16269    }
16270    let Some(declaration) = declaration else {
16271        return Some(DispatchExtensibility::Open);
16272    };
16273
16274    let mut saw_virtual_boundary = false;
16275    let mut stack = vec![declaration];
16276    while let Some(node) = stack.pop() {
16277        match node.kind() {
16278            "compound_statement" | "field_declaration_list" => continue,
16279            "final" | "final_specifier" => return Some(DispatchExtensibility::Closed),
16280            "virtual"
16281            | "override"
16282            | "virtual_specifier"
16283            | "pure_virtual_clause"
16284            | "template_parameter_list"
16285            | "template_method"
16286            | "template_function"
16287            | "ERROR" => saw_virtual_boundary = true,
16288            _ => {}
16289        }
16290        let mut cursor = node.walk();
16291        stack.extend(node.children(&mut cursor));
16292    }
16293
16294    if saw_virtual_boundary {
16295        return Some(DispatchExtensibility::Open);
16296    }
16297    if let Some(body) = class_body {
16298        // C++ inherits virtual-ness, so a member written without a `virtual`
16299        // keyword is still virtual when a base declares it. Whether some base
16300        // does is not decidable here, so a base clause keeps the answer open.
16301        let has_base_clause = ancestry
16302            .parent(body)
16303            .and_then(cpp_class_like_base_clause)
16304            .is_some();
16305        return Some(if has_base_clause {
16306            DispatchExtensibility::Open
16307        } else {
16308            DispatchExtensibility::Closed
16309        });
16310    }
16311    if cpp_callable_declarator_is_qualified(Some(function_declarator)) {
16312        return None;
16313    }
16314    // A namespace or file scope callable is reached by name, never through a
16315    // receiver, so no other body can be selected for a call to it.
16316    Some(DispatchExtensibility::Closed)
16317}
16318
16319fn cpp_callable_linkage<'tree>(
16320    declaration: Node<'tree>,
16321    source: &str,
16322    ancestry: &ParentIndex<'tree>,
16323) -> CallableLinkage {
16324    let mut enclosed_by_class = false;
16325    let mut current = ancestry.parent(declaration);
16326    while let Some(node) = current {
16327        if node.kind() == "namespace_definition"
16328            && node
16329                .child_by_field_name("name")
16330                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16331        {
16332            return CallableLinkage::Internal;
16333        }
16334        if matches!(
16335            node.kind(),
16336            "class_specifier" | "struct_specifier" | "union_specifier"
16337        ) {
16338            if node
16339                .child_by_field_name("name")
16340                .is_none_or(|name| normalize_cpp_whitespace(node_text(name, source)).is_empty())
16341            {
16342                return CallableLinkage::Internal;
16343            }
16344            enclosed_by_class = true;
16345        }
16346        // An export macro between `class` and the class name can make
16347        // tree-sitter parse the entire class as a function definition. The
16348        // structured recovery proves that this container is a named class
16349        // scope, not a local callable scope.
16350        if node.kind() == "lambda_expression"
16351            || node.kind() == "function_definition"
16352                && !is_recovered_exported_class_container(node, source)
16353        {
16354            return CallableLinkage::Internal;
16355        }
16356        current = ancestry.parent(node);
16357    }
16358
16359    if enclosed_by_class {
16360        return CallableLinkage::External;
16361    }
16362
16363    if cpp_declares_static_storage_class(declaration, source) {
16364        CallableLinkage::Internal
16365    } else {
16366        CallableLinkage::External
16367    }
16368}
16369
16370fn cpp_callable_return_type_text<'tree>(
16371    function_declarator: Node<'tree>,
16372    source: &str,
16373    ancestry: &ParentIndex<'tree>,
16374) -> Option<String> {
16375    if cpp_callable_is_structural_constructor(function_declarator, source, ancestry) {
16376        return None;
16377    }
16378    if let Some((return_type, _)) =
16379        cpp_macro_displaced_callable_parts(function_declarator, source, ancestry)
16380    {
16381        let text = normalize_cpp_whitespace(node_text(return_type, source));
16382        return (!text.is_empty()).then_some(text);
16383    }
16384    let mut cursor = function_declarator.walk();
16385    if let Some(trailing) = function_declarator
16386        .named_children(&mut cursor)
16387        .find(|child| child.kind() == "trailing_return_type")
16388        && let Some(type_descriptor) = trailing.named_child(0)
16389    {
16390        let text = normalize_cpp_whitespace(node_text(type_descriptor, source));
16391        if !text.is_empty() {
16392            return Some(text);
16393        }
16394    }
16395
16396    let mut current = function_declarator;
16397    let mut indirection = String::new();
16398    while let Some(parent) = ancestry.parent(current) {
16399        if matches!(
16400            parent.kind(),
16401            "function_definition" | "declaration" | "field_declaration"
16402        ) {
16403            let type_node = parent.child_by_field_name("type")?;
16404            if cpp_declaration_return_type_is_macro_decorated(parent, type_node, source) {
16405                return None;
16406            }
16407            let base = normalize_cpp_whitespace(node_text(type_node, source));
16408            return (!base.is_empty()).then(|| format!("{base}{indirection}"));
16409        }
16410        let wraps_current_declarator = parent.child_by_field_name("declarator") == Some(current)
16411            || (matches!(parent.kind(), "pointer_declarator" | "reference_declarator")
16412                && parent.named_child_count() == 1
16413                && parent.named_child(0) == Some(current));
16414        if wraps_current_declarator {
16415            match parent.kind() {
16416                "pointer_declarator" => indirection.push('*'),
16417                "reference_declarator" => {
16418                    let reference = parent
16419                        .children(&mut parent.walk())
16420                        .find(|child| !child.is_named())
16421                        .map(|child| node_text(child, source))
16422                        .unwrap_or("&");
16423                    indirection.push_str(reference);
16424                }
16425                "init_declarator" | "parenthesized_declarator" => {}
16426                _ => return None,
16427            }
16428            current = parent;
16429            continue;
16430        }
16431        return None;
16432    }
16433    None
16434}
16435
16436fn cpp_callable_arity_from_slots(slots: &[CppParameterSlot<'_>]) -> CallableArity {
16437    let mut required = 0;
16438    let mut total = 0;
16439    let mut repeated = false;
16440    for slot in slots {
16441        match slot {
16442            CppParameterSlot::Declared {
16443                required: is_required,
16444                variadic,
16445                ..
16446            } => {
16447                total += 1;
16448                if *is_required && !*variadic {
16449                    required += 1;
16450                }
16451                repeated |= *variadic;
16452            }
16453            CppParameterSlot::Ellipsis => repeated = true,
16454        }
16455    }
16456    CallableArity::new(required, total, repeated)
16457}
16458
16459/// The pinned grammar's recovery of a Clang Blocks formal `Ret (^name)(Args)`:
16460/// the return type and the caret-less `(name)` read as a `macro_type_specifier`
16461/// whose `^` is the only recovery node, and `(Args)` as the declarator.
16462struct CppBlockPointerFormal<'tree> {
16463    result: Node<'tree>,
16464    name: Node<'tree>,
16465}
16466
16467fn cpp_block_pointer_formal(parameter: Node<'_>) -> Option<CppBlockPointerFormal<'_>> {
16468    if parameter.kind() != "parameter_declaration" {
16469        return None;
16470    }
16471    let ty = parameter.child_by_field_name("type")?;
16472    let declarator = parameter.child_by_field_name("declarator")?;
16473    if ty.kind() != "macro_type_specifier" || declarator.kind() != "abstract_function_declarator" {
16474        return None;
16475    }
16476    let mut cursor = ty.walk();
16477    let children = ty.children(&mut cursor).collect::<Vec<_>>();
16478    let [result, open, caret, descriptor, close] = children.as_slice() else {
16479        return None;
16480    };
16481    let mut caret_cursor = caret.walk();
16482    let caret_children = caret.children(&mut caret_cursor).collect::<Vec<_>>();
16483    let name = descriptor.child_by_field_name("type")?;
16484    (ty.child_by_field_name("name") == Some(*result)
16485        && result.kind() == "identifier"
16486        && open.kind() == "("
16487        && caret.is_error()
16488        && matches!(caret_children.as_slice(), [token] if token.kind() == "^")
16489        && descriptor.kind() == "type_descriptor"
16490        && ty.child_by_field_name("type") == Some(*descriptor)
16491        && descriptor.named_child_count() == 1
16492        && name.kind() == "type_identifier"
16493        && !descriptor.has_error()
16494        && close.kind() == ")"
16495        && !declarator.has_error())
16496    .then_some(CppBlockPointerFormal {
16497        result: *result,
16498        name,
16499    })
16500}
16501
16502/// Whether `recovery` is a recovery node the formal-role reader proves safe.
16503/// Two exact shapes are admitted; every other ERROR or MISSING node leaves the
16504/// formal layout unavailable:
16505/// - the `^` of a Clang Blocks formal, see [`cpp_block_pointer_formal`];
16506/// - the MISSING type of a braced default argument `T x = {}`, which the
16507///   grammar reads as a `compound_literal_expression`. The default value is
16508///   not part of a formal's type, name or optionality.
16509fn cpp_recovery_is_proven_formal_shape(recovery: Node<'_>) -> bool {
16510    let Some(parent) = recovery.parent() else {
16511        return false;
16512    };
16513    if recovery.is_error() {
16514        return parent.kind() == "macro_type_specifier"
16515            && parent
16516                .parent()
16517                .is_some_and(|parameter| cpp_block_pointer_formal(parameter).is_some());
16518    }
16519    recovery.is_missing()
16520        && recovery.kind() == "type_identifier"
16521        && parent.kind() == "compound_literal_expression"
16522        && parent.child_by_field_name("type") == Some(recovery)
16523        && parent
16524            .child_by_field_name("value")
16525            .is_some_and(|value| value.kind() == "initializer_list" && !value.has_error())
16526        && parent.parent().is_some_and(|optional| {
16527            optional.kind() == "optional_parameter_declaration"
16528                && optional.child_by_field_name("default_value") == Some(parent)
16529        })
16530}
16531
16532/// Whether a parameter list holds a recovery node that is not a proven shape.
16533fn cpp_parameter_list_has_unproven_recovery(
16534    list: Node<'_>,
16535    scope_step: &mut impl FnMut() -> bool,
16536) -> Result<bool, CppFormalRoleError> {
16537    let mut pending = vec![list];
16538    while let Some(node) = pending.pop() {
16539        if !scope_step() {
16540            return Err(CppFormalRoleError::Stopped);
16541        }
16542        if !node.has_error() {
16543            continue;
16544        }
16545        if node.is_error() || node.is_missing() {
16546            if !cpp_recovery_is_proven_formal_shape(node) {
16547                return Ok(true);
16548            }
16549            continue;
16550        }
16551        pending.extend(node.children(&mut node.walk()));
16552    }
16553    Ok(false)
16554}
16555
16556/// A grammar role for one direct child of a C or C++ parameter list.
16557/// Invalid and stopped classifications are separate from a valid empty list.
16558#[derive(Clone, Copy)]
16559pub enum CppFormalRole<'tree> {
16560    Declared {
16561        parameter: Node<'tree>,
16562        name: Option<Node<'tree>>,
16563        receiver: bool,
16564        required: bool,
16565        variadic: bool,
16566    },
16567    Void,
16568    Ellipsis,
16569    Other,
16570}
16571
16572#[derive(Clone, Copy)]
16573pub enum CppFormalRoleError {
16574    Invalid,
16575    Stopped,
16576}
16577
16578/// Classify a structurally selected list child. A bare `void` is valid only
16579/// when it is the sole parameter; a typed pack keeps its declaration node.
16580pub fn cpp_formal_role_with_step<'tree>(
16581    child: Node<'tree>,
16582    source: &str,
16583    mut scope_step: impl FnMut() -> bool,
16584) -> Result<CppFormalRole<'tree>, CppFormalRoleError> {
16585    if !scope_step() {
16586        return Err(CppFormalRoleError::Stopped);
16587    }
16588    let Some(list) = child.parent() else {
16589        return Err(CppFormalRoleError::Invalid);
16590    };
16591    if !matches!(list.kind(), "parameter_list" | "parameters")
16592        || child.is_missing()
16593        || cpp_parameter_list_has_unproven_recovery(list, &mut scope_step)?
16594    {
16595        return Err(CppFormalRoleError::Invalid);
16596    }
16597    let (parameter, receiver) = if child.kind() == "explicit_object_parameter_declaration" {
16598        let mut selected = None;
16599        for nested in child.named_children(&mut child.walk()) {
16600            if !scope_step() {
16601                return Err(CppFormalRoleError::Stopped);
16602            }
16603            if nested.kind() == "parameter_declaration" && selected.replace(nested).is_some() {
16604                return Err(CppFormalRoleError::Invalid);
16605            }
16606        }
16607        (selected.ok_or(CppFormalRoleError::Invalid)?, true)
16608    } else {
16609        (child, false)
16610    };
16611    if !scope_step() {
16612        return Err(CppFormalRoleError::Stopped);
16613    }
16614    match parameter.kind() {
16615        "parameter_declaration"
16616        | "optional_parameter_declaration"
16617        | "variadic_parameter_declaration" => {
16618            // The list walk above already refused every recovery node that is
16619            // not an exact proven shape, so only field presence remains.
16620            let ty = parameter
16621                .child_by_field_name("type")
16622                .ok_or(CppFormalRoleError::Invalid)?;
16623            if !scope_step() {
16624                return Err(CppFormalRoleError::Stopped);
16625            }
16626            let declarator = parameter.child_by_field_name("declarator");
16627            if !scope_step() {
16628                return Err(CppFormalRoleError::Stopped);
16629            }
16630            if parameter.kind() == "variadic_parameter_declaration" && declarator.is_none() {
16631                return Err(CppFormalRoleError::Invalid);
16632            }
16633            if parameter.kind() == "optional_parameter_declaration" {
16634                if !scope_step() {
16635                    return Err(CppFormalRoleError::Stopped);
16636                }
16637                if parameter.child_by_field_name("default_value").is_none() {
16638                    return Err(CppFormalRoleError::Invalid);
16639                }
16640            }
16641            if declarator.is_none()
16642                && ty.kind() == "primitive_type"
16643                && node_text(ty, source) == "void"
16644            {
16645                if parameter.kind() != "parameter_declaration" || receiver {
16646                    return Err(CppFormalRoleError::Invalid);
16647                }
16648                let mut parameters = 0;
16649                for sibling in list.children(&mut list.walk()) {
16650                    if !scope_step() {
16651                        return Err(CppFormalRoleError::Stopped);
16652                    }
16653                    if matches!(
16654                        sibling.kind(),
16655                        "parameter_declaration"
16656                            | "optional_parameter_declaration"
16657                            | "variadic_parameter_declaration"
16658                            | "explicit_object_parameter_declaration"
16659                            | "variadic_parameter"
16660                            | "..."
16661                    ) {
16662                        parameters += 1;
16663                    }
16664                }
16665                return if parameters == 1 {
16666                    Ok(CppFormalRole::Void)
16667                } else {
16668                    Err(CppFormalRoleError::Invalid)
16669                };
16670            }
16671            let name = if parameter.kind() == "variadic_parameter_declaration" {
16672                let mut pending = declarator.into_iter().collect::<Vec<_>>();
16673                let mut found = None;
16674                while let Some(node) = pending.pop() {
16675                    if !scope_step() {
16676                        return Err(CppFormalRoleError::Stopped);
16677                    }
16678                    if node.kind() == "identifier" {
16679                        found = Some(node);
16680                        break;
16681                    }
16682                    let children = node.named_children(&mut node.walk()).collect::<Vec<_>>();
16683                    for child in children.into_iter().rev() {
16684                        if !scope_step() {
16685                            return Err(CppFormalRoleError::Stopped);
16686                        }
16687                        pending.push(child);
16688                    }
16689                }
16690                found
16691            } else {
16692                if let Some(declarator) = declarator {
16693                    let mut pending = vec![declarator];
16694                    while let Some(node) = pending.pop() {
16695                        if !scope_step() {
16696                            return Err(CppFormalRoleError::Stopped);
16697                        }
16698                        for child in node.named_children(&mut node.walk()) {
16699                            if !scope_step() {
16700                                return Err(CppFormalRoleError::Stopped);
16701                            }
16702                            pending.push(child);
16703                        }
16704                    }
16705                }
16706                match cpp_block_pointer_formal(parameter) {
16707                    Some(block) => Some(block.name),
16708                    None => declarator.and_then(crate::structural::declarator_name_node),
16709                }
16710            };
16711            Ok(CppFormalRole::Declared {
16712                parameter,
16713                name,
16714                receiver,
16715                required: parameter.kind() != "optional_parameter_declaration",
16716                variadic: parameter.kind() == "variadic_parameter_declaration",
16717            })
16718        }
16719        "variadic_parameter" | "..." => Ok(CppFormalRole::Ellipsis),
16720        "(" | ")" | "," | "comment" => Ok(CppFormalRole::Other),
16721        _ => Err(CppFormalRoleError::Invalid),
16722    }
16723}
16724
16725/// One invocation parameter; explicit object and lone void are excluded.
16726#[derive(Clone, Copy)]
16727enum CppParameterSlot<'tree> {
16728    Declared {
16729        parameter: Node<'tree>,
16730        name: Option<Node<'tree>>,
16731        required: bool,
16732        variadic: bool,
16733    },
16734    Ellipsis,
16735}
16736
16737fn cpp_callable_parameter_slots<'tree>(
16738    parameters_node: Node<'tree>,
16739    source: &str,
16740) -> Result<Vec<CppParameterSlot<'tree>>, CppFormalRoleError> {
16741    if parameters_node.is_missing()
16742        || cpp_parameter_list_has_unproven_recovery(parameters_node, &mut || true)?
16743    {
16744        return Err(CppFormalRoleError::Invalid);
16745    }
16746    let mut slots = Vec::new();
16747    let mut cursor = parameters_node.walk();
16748    for child in parameters_node.children(&mut cursor) {
16749        match cpp_formal_role_with_step(child, source, || true) {
16750            Ok(CppFormalRole::Declared {
16751                parameter,
16752                name,
16753                receiver: false,
16754                required,
16755                variadic,
16756            }) => {
16757                slots.push(CppParameterSlot::Declared {
16758                    parameter,
16759                    name,
16760                    required,
16761                    variadic,
16762                });
16763            }
16764            Ok(CppFormalRole::Ellipsis) => slots.push(CppParameterSlot::Ellipsis),
16765            Ok(
16766                CppFormalRole::Declared { receiver: true, .. }
16767                | CppFormalRole::Void
16768                | CppFormalRole::Other,
16769            ) => {}
16770            Err(error) => return Err(error),
16771        }
16772    }
16773    Ok(slots)
16774}
16775
16776fn cpp_callable_parameter_types_from_slots(
16777    slots: &[CppParameterSlot<'_>],
16778    source: &str,
16779) -> Vec<String> {
16780    slots
16781        .iter()
16782        .map(|slot| match slot {
16783            CppParameterSlot::Declared { parameter, .. } => cpp_parameter_type(*parameter, source),
16784            CppParameterSlot::Ellipsis => "...".to_string(),
16785        })
16786        .collect()
16787}
16788
16789pub(crate) fn cpp_callable_arity(parameters_node: Node<'_>, source: &str) -> Option<CallableArity> {
16790    cpp_callable_parameter_slots(parameters_node, source)
16791        .ok()
16792        .map(|slots| cpp_callable_arity_from_slots(&slots))
16793}
16794
16795pub(crate) fn cpp_callable_parameter_types(
16796    parameters_node: Node<'_>,
16797    source: &str,
16798) -> Option<Vec<String>> {
16799    cpp_callable_parameter_slots(parameters_node, source)
16800        .ok()
16801        .map(|slots| cpp_callable_parameter_types_from_slots(&slots, source))
16802}
16803
16804/// Return the written name of each callable parameter in invocation order.
16805/// C and C++ permit declarations without names; those slots deliberately stay
16806/// `None` inside the returned vector. An unavailable list returns outer `None`.
16807pub(crate) fn cpp_callable_parameter_names(
16808    parameters_node: Node<'_>,
16809    source: &str,
16810) -> Option<Vec<Option<String>>> {
16811    Some(
16812        cpp_callable_parameter_slots(parameters_node, source)
16813            .ok()?
16814            .into_iter()
16815            .map(|slot| match slot {
16816                CppParameterSlot::Declared { name, .. } => {
16817                    name.and_then(|name| direct_identifier_name(name, source))
16818                }
16819                CppParameterSlot::Ellipsis => None,
16820            })
16821            .collect(),
16822    )
16823}
16824
16825/// One callable parameter's parser-derived type.
16826///
16827/// A rendered spelling such as `const T&` is a source text, not a type name. A
16828/// consumer that must publish a type into a structured model - a semantic-pack
16829/// type reference, for example - reads this instead.
16830#[derive(Debug, Clone, PartialEq, Eq)]
16831pub enum CppParameterType {
16832    /// The written type reduced to a structured identity. C++ cv-qualifiers
16833    /// have no place in that model and are not represented.
16834    Structured(StructuredTypeIdentity),
16835    /// A `...` pack, which declares no parameter type at all.
16836    Ellipsis,
16837    /// A written type with no structured reduction, such as a macro-obscured,
16838    /// `decltype`-computed, or function-pointer parameter.
16839    Unstructured,
16840}
16841
16842/// The structured type of each invocation parameter, in declaration order.
16843///
16844/// The result is index-parallel with the rendered
16845/// [`SignatureMetadata::callable_parameter_types`] spellings of the same
16846/// callable. Outer `None` means the grammar did not prove a complete list;
16847/// `Some(Vec::new())` proves a valid zero-parameter list.
16848pub fn cpp_callable_parameter_type_identities<'tree>(
16849    function_declarator: Node<'tree>,
16850    source: &str,
16851    ancestry: &ParentIndex<'tree>,
16852) -> Option<Vec<CppParameterType>> {
16853    let parameters_node = function_declarator.child_by_field_name("parameters")?;
16854    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16855    Some(
16856        cpp_callable_parameter_slots(parameters_node, source)
16857            .ok()?
16858            .into_iter()
16859            .map(|slot| match slot {
16860                CppParameterSlot::Ellipsis => CppParameterType::Ellipsis,
16861                CppParameterSlot::Declared { parameter, .. } => {
16862                    cpp_parameter_type_identity(parameter, source, &lexical_scope)
16863                        .map_or(CppParameterType::Unstructured, CppParameterType::Structured)
16864                }
16865            })
16866            .collect(),
16867    )
16868}
16869
16870/// The parser-derived type of one declared object or parameter.
16871///
16872/// The declaration owns the base `type` field while the individual declarator
16873/// owns pointer, reference, and array wrappers. Keeping both nodes explicit
16874/// lets callers distinguish several declarators in one declaration without
16875/// reparsing a rendered signature.
16876pub fn cpp_declaration_type_identity<'tree>(
16877    declaration: Node<'tree>,
16878    declarator: Node<'tree>,
16879    source: &str,
16880    ancestry: &ParentIndex<'tree>,
16881) -> Option<StructuredTypeIdentity> {
16882    let lexical_scope = cpp_callable_lexical_scope(declarator, source, ancestry);
16883    cpp_declaration_type_identity_in_scope(declaration, Some(declarator), source, &lexical_scope)
16884}
16885
16886fn cpp_parameter_type_identity(
16887    parameter: Node<'_>,
16888    source: &str,
16889    lexical_scope: &[String],
16890) -> Option<StructuredTypeIdentity> {
16891    cpp_declaration_type_identity_in_scope(
16892        parameter,
16893        cpp_parameter_declarator(parameter),
16894        source,
16895        lexical_scope,
16896    )
16897}
16898
16899fn cpp_declaration_type_identity_in_scope(
16900    declaration: Node<'_>,
16901    declarator: Option<Node<'_>>,
16902    source: &str,
16903    lexical_scope: &[String],
16904) -> Option<StructuredTypeIdentity> {
16905    let type_node = declaration.child_by_field_name("type")?;
16906    let identity = cpp_structured_type_identity(type_node, source, lexical_scope)?;
16907    if let Some(declarator) = declarator {
16908        return cpp_apply_structured_type_wrappers(
16909            identity,
16910            cpp_structured_declarator_wrappers(declarator)?,
16911        );
16912    }
16913    Some(identity)
16914}
16915
16916/// The comparable shape of each invocation parameter, in declaration order.
16917///
16918/// The result is index-parallel with
16919/// [`cpp_callable_parameter_type_identities`]; a parameter that admits no
16920/// comparable shape is [`CppComparableSlot::Unstructured`], which a comparison
16921/// must treat as evidence of nothing rather than as agreement. Outer `None`
16922/// means the grammar did not prove the parameter list.
16923pub fn cpp_comparable_parameter_shapes<'tree>(
16924    function_declarator: Node<'tree>,
16925    source: &str,
16926    ancestry: &ParentIndex<'tree>,
16927) -> Option<Vec<CppComparableSlot>> {
16928    let parameters_node = function_declarator.child_by_field_name("parameters")?;
16929    let lexical_scope = cpp_callable_lexical_scope(function_declarator, source, ancestry);
16930    Some(
16931        cpp_callable_parameter_slots(parameters_node, source)
16932            .ok()?
16933            .into_iter()
16934            .map(|slot| match slot {
16935                CppParameterSlot::Ellipsis => CppComparableSlot::Ellipsis,
16936                CppParameterSlot::Declared { parameter, .. } => {
16937                    cpp_comparable_parameter(parameter, source, &lexical_scope)
16938                        .map_or(CppComparableSlot::Unstructured, CppComparableSlot::Shape)
16939                }
16940            })
16941            .collect(),
16942    )
16943}
16944
16945fn cpp_comparable_parameter(
16946    parameter: Node<'_>,
16947    source: &str,
16948    lexical_scope: &[String],
16949) -> Option<CppComparableParameter> {
16950    let type_node = parameter.child_by_field_name("type")?;
16951    let levels = match cpp_parameter_declarator(parameter) {
16952        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
16953        None => Vec::new(),
16954    };
16955    let mut shape = cpp_comparable_type_shape(
16956        type_node,
16957        cpp_cv_qualifiers(parameter, source),
16958        levels,
16959        source,
16960        lexical_scope,
16961    )?;
16962    shape.adjust_parameter_top_level();
16963    Some(shape)
16964}
16965
16966/// The `const` and `volatile` qualifiers written as direct named children of
16967/// `node`.
16968///
16969/// The grammar exposes `type_qualifier` as a non-field named child in exactly
16970/// the three places a parameter's qualifiers can be written: on the
16971/// `parameter_declaration` itself (the base type), on a `type_descriptor`
16972/// (inside a template argument list), and on each `pointer_declarator` level
16973/// (the pointer object). Every other qualifier the grammar admits - `restrict`
16974/// and friends - takes no part in C++ type identity, the same filter
16975/// `cpp_parameter_type` applies to the rendered spelling (#1827).
16976fn cpp_cv_qualifiers(node: Node<'_>, source: &str) -> CppCvQualifiers {
16977    let mut qualifiers = CppCvQualifiers::default();
16978    let mut cursor = node.walk();
16979    for child in node.named_children(&mut cursor) {
16980        if child.kind() != "type_qualifier" {
16981            continue;
16982        }
16983        match node_text(child, source) {
16984            "const" => qualifiers.konst = true,
16985            "volatile" => qualifiers.volatil = true,
16986            _ => {}
16987        }
16988    }
16989    qualifiers
16990}
16991
16992#[derive(Clone, Copy, Default)]
16993struct CppCvQualifiers {
16994    konst: bool,
16995    volatil: bool,
16996}
16997
16998impl CppCvQualifiers {
16999    fn union(self, other: Self) -> Self {
17000        Self {
17001            konst: self.konst || other.konst,
17002            volatil: self.volatil || other.volatil,
17003        }
17004    }
17005}
17006
17007/// One pointer, reference or array level a declarator chain adds.
17008#[derive(Clone, Copy)]
17009enum CppComparableLevel {
17010    Pointer { konst: bool, volatil: bool },
17011    Reference,
17012    Array,
17013}
17014
17015/// The levels `declarator` adds, outermost written level first.
17016///
17017/// C++ declarator syntax binds inside out: the level written closest to the
17018/// declared name is the outermost type constructor, and tree-sitter nests it
17019/// deepest. `int *a[3]` therefore yields `[Pointer, Array]`, which the builder
17020/// applies in order to reach "array of pointer to int", and the qualifier of
17021/// `int * const *p` is read on the level it was written next to, the inner
17022/// pointer of the resulting type.
17023///
17024/// A declarator chain that names a function type - a function-pointer
17025/// parameter - has no comparable shape and reports `None`, matching the
17026/// structured identity channel.
17027fn cpp_comparable_declarator_levels(
17028    declarator: Node<'_>,
17029    source: &str,
17030) -> Option<Vec<CppComparableLevel>> {
17031    let mut levels = Vec::new();
17032    let mut current = declarator;
17033    loop {
17034        match current.kind() {
17035            "pointer_declarator" | "abstract_pointer_declarator" => {
17036                let qualifiers = cpp_cv_qualifiers(current, source);
17037                levels.push(CppComparableLevel::Pointer {
17038                    konst: qualifiers.konst,
17039                    volatil: qualifiers.volatil,
17040                });
17041            }
17042            "reference_declarator" | "abstract_reference_declarator" => {
17043                levels.push(CppComparableLevel::Reference);
17044            }
17045            "array_declarator" | "abstract_array_declarator" => {
17046                levels.push(CppComparableLevel::Array);
17047            }
17048            "parenthesized_declarator" | "abstract_parenthesized_declarator" => {}
17049            "identifier" | "field_identifier" | "type_identifier" => return Some(levels),
17050            _ => return None,
17051        }
17052        let Some(next) = cpp_nested_declarator(current) else {
17053            return Some(levels);
17054        };
17055        current = next;
17056    }
17057}
17058
17059/// Reduce one written type to a comparable arena.
17060///
17061/// The walk is the work-stack shape `cpp_structured_type_identity` uses, with
17062/// two additions: each visited type node carries the cv-qualifiers written on
17063/// it, and declarator levels arrive as a prepared list rather than being
17064/// rediscovered inside the walk.
17065fn cpp_comparable_type_shape(
17066    type_node: Node<'_>,
17067    qualifiers: CppCvQualifiers,
17068    levels: Vec<CppComparableLevel>,
17069    source: &str,
17070    lexical_scope: &[String],
17071) -> Option<CppComparableParameter> {
17072    enum Work<'tree> {
17073        Visit {
17074            node: Node<'tree>,
17075            qualifiers: CppCvQualifiers,
17076        },
17077        ApplyLevels(Vec<CppComparableLevel>),
17078        BuildGeneric {
17079            argument_count: usize,
17080        },
17081    }
17082
17083    let mut nodes: Vec<CppComparableNode> = Vec::new();
17084    let mut values: Vec<usize> = Vec::new();
17085    let mut work = vec![
17086        Work::ApplyLevels(levels),
17087        Work::Visit {
17088            node: type_node,
17089            qualifiers,
17090        },
17091    ];
17092    while let Some(next) = work.pop() {
17093        match next {
17094            Work::Visit { node, qualifiers } => match node.kind() {
17095                "type_descriptor" => {
17096                    let inner_type = node
17097                        .child_by_field_name("type")
17098                        .or_else(|| node.named_child(0))?;
17099                    let mut cursor = node.walk();
17100                    let declarator = node.child_by_field_name("declarator").or_else(|| {
17101                        node.named_children(&mut cursor).find(|child| {
17102                            child.id() != inner_type.id() && child.kind() != "type_qualifier"
17103                        })
17104                    });
17105                    let levels = match declarator {
17106                        Some(declarator) => cpp_comparable_declarator_levels(declarator, source)?,
17107                        None => Vec::new(),
17108                    };
17109                    work.push(Work::ApplyLevels(levels));
17110                    work.push(Work::Visit {
17111                        node: inner_type,
17112                        qualifiers: qualifiers.union(cpp_cv_qualifiers(node, source)),
17113                    });
17114                }
17115                "sized_type_specifier" => {
17116                    // `unsigned char` is one primitive type whose components are
17117                    // partly unnamed tokens, so the whole specifier is its own
17118                    // name component. Reducing it to the `type` child would make
17119                    // `f(unsigned char)` and `f(char)` compare equal.
17120                    let name = StructuredTypeName::new(
17121                        vec![normalize_cpp_whitespace(node_text(node, source))],
17122                        lexical_scope.to_vec(),
17123                        false,
17124                    )?;
17125                    values.push(cpp_push_comparable_node(
17126                        &mut nodes,
17127                        CppComparableNode::Named {
17128                            name,
17129                            primitive: true,
17130                            konst: qualifiers.konst,
17131                            volatil: qualifiers.volatil,
17132                        },
17133                    ));
17134                }
17135                "qualified_identifier"
17136                | "scoped_identifier"
17137                | "scoped_type_identifier"
17138                | "type_identifier"
17139                | "field_identifier"
17140                | "identifier"
17141                | "namespace_identifier"
17142                | "primitive_type"
17143                | "template_type" => {
17144                    let name = cpp_structured_named_type(node, source, lexical_scope)?;
17145                    values.push(cpp_push_comparable_node(
17146                        &mut nodes,
17147                        CppComparableNode::Named {
17148                            name,
17149                            primitive: node.kind() == "primitive_type",
17150                            konst: qualifiers.konst,
17151                            volatil: qualifiers.volatil,
17152                        },
17153                    ));
17154                    if let Some(arguments_node) = cpp_comparable_template_arguments(node) {
17155                        let mut cursor = arguments_node.walk();
17156                        let arguments = arguments_node
17157                            .named_children(&mut cursor)
17158                            .filter(|child| !child.is_extra() && child.kind() != "comment")
17159                            .collect::<Vec<_>>();
17160                        work.push(Work::BuildGeneric {
17161                            argument_count: arguments.len(),
17162                        });
17163                        work.extend(arguments.into_iter().rev().map(|argument| Work::Visit {
17164                            node: argument,
17165                            qualifiers: CppCvQualifiers::default(),
17166                        }));
17167                    }
17168                }
17169                _ => {
17170                    let inner = node.child_by_field_name("type").or_else(|| {
17171                        (node.named_child_count() == 1)
17172                            .then(|| node.named_child(0))
17173                            .flatten()
17174                    })?;
17175                    work.push(Work::Visit {
17176                        node: inner,
17177                        qualifiers,
17178                    });
17179                }
17180            },
17181            Work::ApplyLevels(levels) => {
17182                let mut root = values.pop()?;
17183                for level in levels {
17184                    let node = match level {
17185                        CppComparableLevel::Pointer { konst, volatil } => {
17186                            CppComparableNode::Pointer {
17187                                inner: root,
17188                                konst,
17189                                volatil,
17190                            }
17191                        }
17192                        CppComparableLevel::Reference => {
17193                            CppComparableNode::Reference { inner: root }
17194                        }
17195                        CppComparableLevel::Array => CppComparableNode::Array { inner: root },
17196                    };
17197                    root = cpp_push_comparable_node(&mut nodes, node);
17198                }
17199                values.push(root);
17200            }
17201            Work::BuildGeneric { argument_count } => {
17202                let value_count = argument_count.checked_add(1)?;
17203                let start = values.len().checked_sub(value_count)?;
17204                let mut built = values.split_off(start);
17205                let base = built.remove(0);
17206                values.push(cpp_push_comparable_node(
17207                    &mut nodes,
17208                    CppComparableNode::Generic {
17209                        base,
17210                        arguments: built,
17211                    },
17212                ));
17213            }
17214        }
17215    }
17216    let root = (values.len() == 1).then(|| values.pop()).flatten()?;
17217    debug_assert_eq!(
17218        root,
17219        nodes.len().saturating_sub(1),
17220        "comparable nodes are appended in post-order, so the root is the last one"
17221    );
17222    Some(CppComparableParameter::new(nodes, root))
17223}
17224
17225fn cpp_push_comparable_node(nodes: &mut Vec<CppComparableNode>, node: CppComparableNode) -> usize {
17226    nodes.push(node);
17227    nodes.len() - 1
17228}
17229
17230/// The template argument list of the name `node` terminates in, if any.
17231///
17232/// `std::vector<int>` writes its arguments on the `name` of a qualified
17233/// identifier, so a walk that stopped at the qualified node would reduce
17234/// `std::vector<const int *>` and `std::vector<int *>` to the same name.
17235fn cpp_comparable_template_arguments(node: Node<'_>) -> Option<Node<'_>> {
17236    let mut current = node;
17237    loop {
17238        match current.kind() {
17239            "template_type" => return current.child_by_field_name("arguments"),
17240            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
17241                current = current.child_by_field_name("name")?;
17242            }
17243            _ => return None,
17244        }
17245    }
17246}
17247
17248/// The callable declarator of the declaration that covers `start_byte`.
17249///
17250/// A consumer that holds a declaration's recorded byte position rather than its
17251/// syntax node - external header extraction, for instance - uses this to reach
17252/// the same `function_declarator` the declaration walk read.
17253pub fn cpp_function_declarator_at(root: Node<'_>, start_byte: usize) -> Option<Node<'_>> {
17254    let mut current = root.descendant_for_byte_range(start_byte, start_byte)?;
17255    loop {
17256        if matches!(
17257            current.kind(),
17258            "declaration" | "field_declaration" | "function_definition"
17259        ) && let Some(declarator) = current
17260            .child_by_field_name("declarator")
17261            .and_then(extract_function_declarator)
17262        {
17263            return Some(declarator);
17264        }
17265        current = current.parent()?;
17266    }
17267}
17268
17269fn cpp_parameter_label_nodes(parameters_node: Node<'_>) -> Vec<Node<'_>> {
17270    let mut labels = Vec::new();
17271    let mut cursor = parameters_node.walk();
17272    for child in parameters_node.children(&mut cursor) {
17273        match child.kind() {
17274            "parameter_declaration" | "optional_parameter_declaration" => {
17275                if let Some(name_node) = child
17276                    .child_by_field_name("declarator")
17277                    .and_then(cpp_declarator_label_node)
17278                {
17279                    labels.push(name_node);
17280                } else {
17281                    labels.push(child);
17282                }
17283            }
17284            "variadic_parameter" | "variadic_parameter_declaration" | "..." => {
17285                labels.push(child);
17286            }
17287            _ => {}
17288        }
17289    }
17290    labels
17291}
17292
17293fn cpp_signature_search_start<'tree>(
17294    signature: &str,
17295    function_declarator: Node<'tree>,
17296    source: &str,
17297    ancestry: &ParentIndex<'tree>,
17298) -> usize {
17299    let Some(enclosing) = enclosing_cpp_declaration_node(function_declarator, ancestry) else {
17300        return 0;
17301    };
17302    let raw = node_text(enclosing, source);
17303    let leading_trim_bytes = raw.len().saturating_sub(raw.trim_start().len());
17304    let offset = function_declarator
17305        .start_byte()
17306        .saturating_sub(enclosing.start_byte())
17307        .saturating_sub(leading_trim_bytes);
17308    offset.min(signature.len())
17309}
17310
17311fn cpp_declarator_label_node(node: Node<'_>) -> Option<Node<'_>> {
17312    match node.kind() {
17313        "identifier" | "field_identifier" => Some(node),
17314        "pointer_declarator" | "reference_declarator" | "parenthesized_declarator" => node
17315            .child_by_field_name("declarator")
17316            .or_else(|| last_named_child(node))
17317            .and_then(cpp_declarator_label_node),
17318        "array_declarator" => node
17319            .child_by_field_name("declarator")
17320            .and_then(cpp_declarator_label_node),
17321        "function_declarator" => node
17322            .child_by_field_name("declarator")
17323            .or_else(|| node.child_by_field_name("name"))
17324            .or_else(|| last_named_child(node))
17325            .and_then(cpp_declarator_label_node),
17326        _ => None,
17327    }
17328}
17329
17330fn cpp_parameter_type(parameter: Node<'_>, source: &str) -> String {
17331    let base_type = parameter
17332        .child_by_field_name("type")
17333        .map(|node| normalize_cpp_whitespace(node_text(node, source)))
17334        .unwrap_or_default();
17335    let declarator = cpp_parameter_declarator(parameter);
17336    // [dcl.fct]/5: after parameter-type adjustment the top-level cv-qualifiers
17337    // are discarded, so `f(const int)` and `f(int)` declare one function. A
17338    // qualifier written next to the parameter's type is only top-level when
17339    // the declarator adds no indirection; behind a pointer, reference or array
17340    // declarator the same qualifier belongs to the pointee, referent or
17341    // element and keeps distinguishing the type (#1827).
17342    let keeps_top_level_cv = declarator.is_some_and(cpp_declarator_adds_indirection);
17343    let mut cursor = parameter.walk();
17344    let qualifiers = parameter
17345        .named_children(&mut cursor)
17346        .filter(|child| child.kind() == "type_qualifier")
17347        .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17348        .filter(|text| keeps_top_level_cv || !matches!(text.as_str(), "const" | "volatile"))
17349        .collect::<Vec<_>>()
17350        .join(" ");
17351    let type_text = match (qualifiers.is_empty(), base_type.is_empty()) {
17352        (true, _) => base_type,
17353        (_, true) => qualifiers,
17354        (false, false) => format!("{qualifiers} {base_type}"),
17355    };
17356    let declarator_suffix = declarator
17357        .map(|node| cpp_declarator_suffix_without_name(node, source))
17358        .unwrap_or_default();
17359
17360    let combined = if type_text.is_empty() {
17361        declarator_suffix
17362    } else if declarator_suffix.is_empty() {
17363        type_text
17364    } else {
17365        format!("{type_text} {declarator_suffix}")
17366    };
17367    normalize_cpp_type_text(&combined)
17368}
17369
17370fn cpp_parameter_declarator(parameter: Node<'_>) -> Option<Node<'_>> {
17371    parameter.child_by_field_name("declarator").or_else(|| {
17372        // Some unnamed prototype parameters expose their abstract declarator
17373        // as a direct named child without the grammar's `declarator` field.
17374        // Recover only the structured abstract-declarator node; the parameter's
17375        // type and qualifiers are distinct children and must not be guessed from
17376        // source text.
17377        cpp_first_abstract_declarator_child(parameter)
17378    })
17379}
17380
17381/// Whether a parameter's declarator chain adds indirection - a pointer,
17382/// reference, array or function declarator - to the parameter's written type.
17383pub(crate) fn cpp_declarator_adds_indirection(declarator: Node<'_>) -> bool {
17384    let mut current = Some(declarator);
17385    while let Some(node) = current {
17386        if cpp_is_member_pointer_declarator(node) {
17387            return true;
17388        }
17389        if matches!(
17390            node.kind(),
17391            "pointer_declarator"
17392                | "abstract_pointer_declarator"
17393                | "reference_declarator"
17394                | "abstract_reference_declarator"
17395                | "array_declarator"
17396                | "abstract_array_declarator"
17397                | "function_declarator"
17398                | "abstract_function_declarator"
17399        ) {
17400            return true;
17401        }
17402        current = cpp_nested_declarator(node);
17403    }
17404    false
17405}
17406
17407fn is_cpp_abstract_declarator(kind: &str) -> bool {
17408    matches!(
17409        kind,
17410        "abstract_pointer_declarator"
17411            | "abstract_reference_declarator"
17412            | "abstract_array_declarator"
17413            | "abstract_function_declarator"
17414            | "abstract_parenthesized_declarator"
17415    )
17416}
17417
17418fn cpp_first_abstract_declarator_child(node: Node<'_>) -> Option<Node<'_>> {
17419    let mut cursor = node.walk();
17420    node.named_children(&mut cursor)
17421        .find(|child| is_cpp_abstract_declarator(child.kind()))
17422}
17423
17424fn cpp_nested_declarator(node: Node<'_>) -> Option<Node<'_>> {
17425    node.child_by_field_name("declarator").or_else(|| {
17426        if is_cpp_abstract_declarator(node.kind()) {
17427            cpp_first_abstract_declarator_child(node)
17428        } else {
17429            // Named declarators historically use their last named child when
17430            // tree-sitter omits the field. Keep that broad fallback for
17431            // attributed, variadic, and recovered named shapes.
17432            last_named_child(node)
17433        }
17434    })
17435}
17436
17437pub(crate) fn reference_declarator_is_rvalue(node: Node<'_>) -> bool {
17438    matches!(
17439        node.kind(),
17440        "reference_declarator" | "abstract_reference_declarator"
17441    ) && node
17442        .children(&mut node.walk())
17443        .any(|child| child.kind() == "&&")
17444}
17445
17446/// Whether `node` is a pointer-to-member declarator, `Left::*cb` or `Left::*`.
17447/// brokk-tree-sitter-cpp 0.24.7 reads one as a `qualified_identifier` whose
17448/// `name` is a pointer declarator, possibly behind further scopes
17449/// (`A::B::*p`), with the owning class in `scope`.
17450fn cpp_is_member_pointer_declarator(node: Node<'_>) -> bool {
17451    let mut current = node;
17452    while current.kind() == "qualified_identifier" {
17453        let Some(name) = current.child_by_field_name("name") else {
17454            return false;
17455        };
17456        if matches!(
17457            name.kind(),
17458            "pointer_declarator" | "abstract_pointer_declarator" | "pointer_type_declarator"
17459        ) {
17460            return true;
17461        }
17462        current = name;
17463    }
17464    false
17465}
17466
17467fn cpp_declarator_suffix_without_name(node: Node<'_>, source: &str) -> String {
17468    match node.kind() {
17469        "identifier" | "field_identifier" => String::new(),
17470        // `Left::*cb` keeps its owning class, so `void (Left::*)()` and
17471        // `void (Right::*)()` stay distinct overloads (#6902 item 6).
17472        "qualified_identifier" if cpp_is_member_pointer_declarator(node) => {
17473            let scope = node
17474                .child_by_field_name("scope")
17475                .map(|scope| normalize_cpp_whitespace(node_text(scope, source)))
17476                .unwrap_or_default();
17477            let name = node
17478                .child_by_field_name("name")
17479                .map(|name| cpp_declarator_suffix_without_name(name, source))
17480                .unwrap_or_default();
17481            format!("{scope}::{name}")
17482        }
17483        "pointer_declarator" | "abstract_pointer_declarator" => {
17484            let inner = cpp_nested_declarator(node)
17485                .map(|child| cpp_declarator_suffix_without_name(child, source))
17486                .unwrap_or_default();
17487            format!("*{inner}")
17488        }
17489        "reference_declarator" | "abstract_reference_declarator" => {
17490            let inner = cpp_nested_declarator(node)
17491                .map(|child| cpp_declarator_suffix_without_name(child, source))
17492                .unwrap_or_default();
17493            let reference = if reference_declarator_is_rvalue(node) {
17494                "&&"
17495            } else {
17496                "&"
17497            };
17498            format!("{reference}{inner}")
17499        }
17500        "array_declarator" | "abstract_array_declarator" => {
17501            let inner = cpp_nested_declarator(node)
17502                .map(|child| cpp_declarator_suffix_without_name(child, source))
17503                .unwrap_or_default();
17504            let size = node
17505                .child_by_field_name("size")
17506                .map(|child| normalize_cpp_whitespace(node_text(child, source)))
17507                .unwrap_or_default();
17508            format!("{inner}[{size}]")
17509        }
17510        "parenthesized_declarator" | "abstract_parenthesized_declarator" => {
17511            let inner = cpp_nested_declarator(node);
17512            inner
17513                .map(|child| format!("({})", cpp_declarator_suffix_without_name(child, source)))
17514                .unwrap_or_default()
17515        }
17516        "function_declarator" | "abstract_function_declarator" => {
17517            let inner = cpp_nested_declarator(node)
17518                .map(|child| cpp_declarator_suffix_without_name(child, source))
17519                .unwrap_or_default();
17520            let params = node
17521                .child_by_field_name("parameters")
17522                .map(|child| cpp_parameter_signature(child, source))
17523                .unwrap_or_else(|| "()".to_string());
17524            format!("{inner}{params}")
17525        }
17526        _ => {
17527            let text = normalize_cpp_whitespace(node_text(node, source));
17528            let name = extract_declarator_name(node, source);
17529            if name.is_empty() {
17530                text
17531            } else {
17532                text.replace(&name, "").trim().to_string()
17533            }
17534        }
17535    }
17536}
17537
17538fn normalize_cpp_qualifier_suffix(suffix: &str) -> String {
17539    collapse_cpp_whitespace(
17540        suffix
17541            .trim()
17542            .trim_start_matches("->")
17543            .trim_start_matches('{')
17544            .trim_end_matches(';'),
17545    )
17546}
17547
17548pub fn normalize_cpp_whitespace(value: &str) -> String {
17549    collapse_cpp_whitespace(value)
17550}
17551
17552fn normalize_cpp_type_text(value: &str) -> String {
17553    collapse_cpp_whitespace(value)
17554        .replace(", ", ",")
17555        .replace(" <", "<")
17556        .replace("< ", "<")
17557        .replace(" >", ">")
17558}
17559
17560fn collapse_cpp_whitespace(value: &str) -> String {
17561    let mut result = String::new();
17562    let mut prev_space = false;
17563    for ch in value.chars() {
17564        if ch.is_whitespace() {
17565            if !prev_space {
17566                result.push(' ');
17567            }
17568            prev_space = true;
17569        } else {
17570            result.push(ch);
17571            prev_space = false;
17572        }
17573    }
17574    result.trim().to_string()
17575}
17576
17577fn cpp_body_node(node: Node<'_>) -> Option<Node<'_>> {
17578    child_by_field_or_named_child_of_kinds(
17579        node,
17580        "body",
17581        &[
17582            "declaration_list",
17583            "field_declaration_list",
17584            "enumerator_list",
17585        ],
17586    )
17587}
17588
17589/// Return a class body's actual closing brace when the parser supplied one.
17590///
17591/// A malformed namespace sentinel can leave a class node carrying unrelated
17592/// parser errors even though its own class body is complete.  `has_error()` is
17593/// therefore too coarse an admission predicate for sentinel ownership.  The
17594/// body list, however, exposes the opening and closing punctuation directly;
17595/// a real (non-missing) final `}` proves that the class did not borrow the
17596/// enclosing namespace's close.  Requiring the body to end before its parent
17597/// container also rejects a recovered node whose body swallowed that outer
17598/// boundary.
17599fn cpp_complete_class_body_close(node: Node<'_>) -> Option<Node<'_>> {
17600    if !matches!(
17601        node.kind(),
17602        "class_specifier" | "struct_specifier" | "union_specifier"
17603    ) {
17604        return None;
17605    }
17606    let body = cpp_body_node(node)?;
17607    if !matches!(body.kind(), "declaration_list" | "field_declaration_list") {
17608        return None;
17609    }
17610    let open = body.child(0)?;
17611    let close = body.child(body.child_count().checked_sub(1)?)?;
17612    if open.kind() != "{"
17613        || open.is_missing()
17614        || close.kind() != "}"
17615        || close.is_missing()
17616        || close.end_byte() != body.end_byte()
17617        || body.end_byte() > node.end_byte()
17618        || node
17619            .parent()
17620            .is_some_and(|parent| body.end_byte() >= parent.end_byte())
17621    {
17622        return None;
17623    }
17624    Some(close)
17625}
17626
17627fn cpp_contains_namespace_definition(node: Node<'_>) -> bool {
17628    let mut stack = vec![node];
17629    while let Some(node) = stack.pop() {
17630        if node.kind() == "namespace_definition" {
17631            return true;
17632        }
17633        push_named_children_reversed(node, &mut stack);
17634    }
17635    false
17636}
17637
17638struct CppNestedNamespaceSentinel<'tree> {
17639    function: Node<'tree>,
17640    body: Node<'tree>,
17641    namespace_components: Vec<String>,
17642}
17643
17644/// Owned structural recovery metadata for a namespace-sentinel region.
17645///
17646/// Tree-sitter puts an `ABSL_NAMESPACE_BEGIN` region in a bogus function body
17647/// instead of the namespace/class scopes that the declaration visitor restores.
17648/// The inverted usage walk has the original CST, so it needs the same ownership
17649/// evidence without borrowing parser nodes across its file scan.  Keep this
17650/// descriptor deliberately source-range based: callers can match a reference
17651/// node by containment and then resolve its structured type spelling in the
17652/// recovered class scope.
17653#[derive(Debug, Clone)]
17654pub struct CppSentinelRecoveredOwner {
17655    pub range: Range,
17656    /// Start of the qualified owner name (`btree<P>::method`).  A leading
17657    /// return type before this byte is looked up from the namespace; parameters,
17658    /// trailing returns, and the body use the member owner scope.
17659    pub owner_name_start_byte: usize,
17660    /// Number of leading components belonging to the namespace rather than
17661    /// the qualified class owner.  A leading return type is looked up before
17662    /// every owner component, not merely before the innermost class.
17663    pub namespace_component_count: usize,
17664    pub scope_components: Vec<String>,
17665}
17666
17667#[derive(Debug, Clone)]
17668pub struct CppSentinelRecoveredClass {
17669    pub namespace_range: Range,
17670    pub namespace_scope_components: Vec<String>,
17671    pub class_range: Range,
17672    /// Full namespace + class path, e.g. `absl,container_internal,btree`.
17673    pub scope_components: Vec<String>,
17674    /// Qualified out-of-line member definitions owned by this class.  Their
17675    /// ranges may extend beyond `class_range` when the malformed sentinel
17676    /// swallowed the namespace close and left definitions as function siblings.
17677    pub owner_ranges: Vec<CppSentinelRecoveredOwner>,
17678}
17679
17680/// Resolve the lexical scope restored for a node in a malformed
17681/// namespace-sentinel region.  Owner spans (out-of-line member definitions)
17682/// outrank class spans, which in turn outrank the surviving namespace body.
17683/// The class ancestor suffix is recovered from the original CST so nested
17684/// members keep their complete `Outer::Inner` owner chain.
17685pub fn cpp_sentinel_recovered_scope_for_node(
17686    node: Node<'_>,
17687    source: &str,
17688    recovered_classes: &[CppSentinelRecoveredClass],
17689) -> Option<Vec<String>> {
17690    let contains =
17691        |range: Range| range.start_byte <= node.start_byte() && range.end_byte >= node.end_byte();
17692    let mut best_owner: Option<&CppSentinelRecoveredOwner> = None;
17693    for recovered in recovered_classes {
17694        for owner in recovered
17695            .owner_ranges
17696            .iter()
17697            .filter(|owner| contains(owner.range))
17698        {
17699            let replace = best_owner.is_none_or(|existing| {
17700                owner.range.end_byte.saturating_sub(owner.range.start_byte)
17701                    < existing
17702                        .range
17703                        .end_byte
17704                        .saturating_sub(existing.range.start_byte)
17705            });
17706            if replace {
17707                best_owner = Some(owner);
17708            }
17709        }
17710    }
17711    if let Some(owner) = best_owner {
17712        let mut scope = owner.scope_components.clone();
17713        if node.start_byte() < owner.owner_name_start_byte {
17714            scope.truncate(owner.namespace_component_count);
17715        }
17716        return Some(scope);
17717    }
17718
17719    let class = recovered_classes
17720        .iter()
17721        .filter(|recovered| contains(recovered.class_range))
17722        .min_by_key(|recovered| {
17723            recovered
17724                .class_range
17725                .end_byte
17726                .saturating_sub(recovered.class_range.start_byte)
17727        });
17728    let class_scope = class.is_some();
17729    let mut scope = if let Some(class) = class {
17730        class.scope_components.clone()
17731    } else {
17732        let namespace = recovered_classes
17733            .iter()
17734            .filter(|recovered| contains(recovered.namespace_range))
17735            .min_by_key(|recovered| {
17736                recovered
17737                    .namespace_range
17738                    .end_byte
17739                    .saturating_sub(recovered.namespace_range.start_byte)
17740            })?;
17741        let mut scope = namespace.namespace_scope_components.clone();
17742        let parser_namespace = cpp_sentinel_recovered_namespace_components(node, &[], source);
17743        let common_prefix = scope
17744            .iter()
17745            .zip(&parser_namespace)
17746            .take_while(|(recovered, parser)| recovered == parser)
17747            .count();
17748        scope.extend(parser_namespace.into_iter().skip(common_prefix));
17749        scope
17750    };
17751    if class_scope {
17752        let mut ancestor_components = Vec::new();
17753        let mut ancestor = node.parent();
17754        while let Some(current) = ancestor {
17755            if matches!(
17756                current.kind(),
17757                "class_specifier" | "struct_specifier" | "union_specifier"
17758            ) && let Some(name) = current.child_by_field_name("name")
17759                && let Some(name_components) = cpp_name_components(name, source)
17760            {
17761                ancestor_components.push(
17762                    name_components
17763                        .into_iter()
17764                        .map(|component| component.name)
17765                        .collect::<Vec<_>>(),
17766                );
17767            }
17768            ancestor = current.parent();
17769        }
17770        ancestor_components.reverse();
17771        let base_len = scope.len();
17772        for component in ancestor_components.into_iter().flatten() {
17773            if scope.len() >= base_len && scope.last() == Some(&component) {
17774                continue;
17775            }
17776            scope.push(component);
17777        }
17778    }
17779    Some(scope)
17780}
17781
17782struct CppSentinelFragmentedClassTail<'tree> {
17783    class_node: Node<'tree>,
17784    template_node: Option<Node<'tree>>,
17785    name: String,
17786    raw_supertypes: Option<Vec<String>>,
17787    fragmented: FragmentedExportBody,
17788    consumed_start: usize,
17789    /// The original node whose direct children after the class open are the
17790    /// class's members: the body of a class node, or the class-prefix `ERROR`
17791    /// itself when the class keyword, name, and `{` are its direct tokens.
17792    member_container: Node<'tree>,
17793}
17794
17795struct CppSentinelFragmentedClassErrorPrefix<'tree> {
17796    name: String,
17797    open: Node<'tree>,
17798    raw_supertypes: Option<Vec<String>>,
17799}
17800
17801struct CppSentinelDirectBodyClassRegion {
17802    namespace_components: Vec<String>,
17803    class_start: usize,
17804    class_start_line: usize,
17805    class_close_end: usize,
17806    class_close_line: usize,
17807    name: String,
17808}
17809
17810fn cpp_sentinel_body_class_candidate<'tree>(
17811    child: Node<'tree>,
17812) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17813    if matches!(
17814        child.kind(),
17815        "class_specifier" | "struct_specifier" | "union_specifier"
17816    ) {
17817        return Some((child, None));
17818    }
17819    if child.kind() != "template_declaration" {
17820        if child.kind() == "declaration" {
17821            return Some((first_class_like_child(child)?, None));
17822        }
17823        return None;
17824    }
17825    let mut cursor = child.walk();
17826    let class_node = child.named_children(&mut cursor).find_map(|candidate| {
17827        if matches!(
17828            candidate.kind(),
17829            "class_specifier" | "struct_specifier" | "union_specifier"
17830        ) {
17831            Some(candidate)
17832        } else if candidate.kind() == "declaration" {
17833            first_class_like_child(candidate)
17834        } else {
17835            None
17836        }
17837    })?;
17838    Some((class_node, Some(child)))
17839}
17840
17841/// Recognize the direct `ERROR(class, name, "{", members...)` prefix left in a
17842/// namespace-sentinel body when a later member macro ends the bogus sentinel
17843/// function before the real class close. The anonymous class/open tokens and
17844/// direct identifier are the structural proof; a retained direct close would
17845/// be an ordinary malformed class rather than the fragmented tail handled here.
17846fn cpp_sentinel_fragmented_class_error_prefix<'tree>(
17847    node: Node<'tree>,
17848    source: &str,
17849) -> Option<CppSentinelFragmentedClassErrorPrefix<'tree>> {
17850    let name = malformed_class_error_owner_name(node, source)?;
17851    let mut cursor = node.walk();
17852    let children = node.children(&mut cursor).collect::<Vec<_>>();
17853    let keyword = children.first()?;
17854    let open_index = children.iter().position(|child| child.kind() == "{")?;
17855    if children[open_index + 1..]
17856        .iter()
17857        .any(|child| child.kind() == "}")
17858    {
17859        return None;
17860    }
17861    let raw_supertypes =
17862        matches!(keyword.kind(), "class" | "struct").then(|| extract_cpp_supertypes(node, source));
17863    Some(CppSentinelFragmentedClassErrorPrefix {
17864        name,
17865        open: children[open_index],
17866        raw_supertypes,
17867    })
17868}
17869
17870fn cpp_sentinel_direct_body_class_candidate<'tree>(
17871    child: Node<'tree>,
17872) -> Option<(Node<'tree>, Option<Node<'tree>>)> {
17873    if let Some(candidate) = cpp_sentinel_body_class_candidate(child) {
17874        return Some(candidate);
17875    }
17876    if child.kind() != "template_declaration" {
17877        return None;
17878    }
17879    let mut cursor = child.walk();
17880    let wrapper = child
17881        .named_children(&mut cursor)
17882        .find(|candidate| candidate.kind() == "function_definition" && candidate.has_error())?;
17883    Some((first_class_like_child(wrapper)?, Some(child)))
17884}
17885
17886fn cpp_sentinel_direct_namespace_components(
17887    function: Node<'_>,
17888    body: Node<'_>,
17889    source: &str,
17890) -> Option<Vec<String>> {
17891    let mut cursor = function.walk();
17892    let children = function
17893        .named_children(&mut cursor)
17894        .filter(|child| child.kind() != "comment" && child.end_byte() <= body.start_byte())
17895        .collect::<Vec<_>>();
17896    let sentinel_index = children.iter().rposition(|child| {
17897        direct_identifier_name(*child, source)
17898            .is_some_and(|name| cpp_export_macro_token(&name) && name.ends_with("NAMESPACE_BEGIN"))
17899    })?;
17900    let mut identifiers = Vec::new();
17901    let mut stack = children[sentinel_index + 1..]
17902        .iter()
17903        .rev()
17904        .copied()
17905        .collect::<Vec<_>>();
17906    while let Some(current) = stack.pop() {
17907        if let Some(name) = direct_identifier_name(current, source) {
17908            identifiers.push(name);
17909            continue;
17910        }
17911        let mut cursor = current.walk();
17912        let children = current.named_children(&mut cursor).collect::<Vec<_>>();
17913        stack.extend(children.into_iter().rev());
17914    }
17915    let [keyword, namespace] = identifiers.as_slice() else {
17916        return None;
17917    };
17918    (keyword == "namespace" && !namespace.is_empty() && !cpp_export_macro_token(namespace))
17919        .then(|| vec![namespace.clone()])
17920}
17921
17922fn cpp_sentinel_namespace_close_follows_class(class_semicolon: Node<'_>, source: &str) -> bool {
17923    let mut sibling = class_semicolon.next_named_sibling();
17924    let namespace_close = loop {
17925        let Some(current) = sibling else {
17926            return false;
17927        };
17928        sibling = current.next_named_sibling();
17929        if current.kind() != "comment" {
17930            break current;
17931        }
17932    };
17933    if !cpp_is_stray_close_brace(namespace_close, source) {
17934        return false;
17935    }
17936    loop {
17937        let Some(current) = sibling else {
17938            return false;
17939        };
17940        sibling = current.next_named_sibling();
17941        if current.kind() == "comment" {
17942            continue;
17943        }
17944        return direct_identifier_name(current, source)
17945            .is_some_and(|name| name.ends_with("NAMESPACE_END"));
17946    }
17947}
17948
17949fn cpp_sentinel_macro_body_class_region<'tree>(
17950    node: Node<'tree>,
17951    source: &str,
17952    ancestry: &ParentIndex<'tree>,
17953) -> Option<CppSentinelDirectBodyClassRegion> {
17954    let (_, None) = cpp_sentinel_macro_parts(node, source)? else {
17955        return None;
17956    };
17957    if node.kind() != "function_definition" || !node.has_error() {
17958        return None;
17959    }
17960    let body = cpp_body_node(node).filter(|body| body.kind() == "compound_statement")?;
17961    let namespace_components = cpp_sentinel_direct_namespace_components(node, body, source)?;
17962    let mut cursor = body.walk();
17963    let candidates = body
17964        .named_children(&mut cursor)
17965        .filter_map(cpp_sentinel_direct_body_class_candidate)
17966        .filter(|(class_node, _)| class_node.has_error() && cpp_body_node(*class_node).is_some())
17967        .collect::<Vec<_>>();
17968    let [(class_node, template_node)] = candidates.as_slice() else {
17969        return None;
17970    };
17971    let original_body = cpp_body_node(*class_node)?;
17972    let name = class_like_name(*class_node, source, ancestry)?;
17973    if name.is_empty() {
17974        return None;
17975    }
17976
17977    let mut sibling = node.next_named_sibling();
17978    let (class_close_start, class_close_end, class_close_line) = loop {
17979        let current = sibling?;
17980        let next = current.next_named_sibling();
17981        if cpp_is_stray_close_brace(current, source)
17982            && next.is_some_and(|next| cpp_is_stray_semicolon(next, source))
17983        {
17984            let semicolon = next.expect("checked above");
17985            if !cpp_sentinel_namespace_close_follows_class(semicolon, source) {
17986                return None;
17987            }
17988            break (
17989                current.start_byte(),
17990                semicolon.end_byte(),
17991                semicolon.end_position().row + 1,
17992            );
17993        }
17994        sibling = next;
17995    };
17996    let reparse_start = template_node.map_or(class_node.start_byte(), |node| node.start_byte());
17997    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
17998    let root = tree.root_node();
17999    let reparsed_template = cpp_sentinel_reparsed_leading_template(root);
18000    // The region reparse is its own tree, so it needs its own parent index;
18001    // the caller's index answers nothing about these nodes.
18002    let reparsed_ancestry = ParentIndex::new(root);
18003    let reparsed =
18004        cpp_sentinel_reparsed_class(root, reparsed_template, source, &reparsed_ancestry)?;
18005    if reparsed.name != name
18006        || reparsed.declaration_node.start_byte() != class_node.start_byte()
18007        || reparsed.body.start_byte() != original_body.start_byte()
18008        || class_close_start <= reparsed.body.end_byte()
18009        || class_close_end <= class_node.end_byte()
18010    {
18011        return None;
18012    }
18013    Some(CppSentinelDirectBodyClassRegion {
18014        namespace_components,
18015        class_start: reparse_start,
18016        class_start_line: template_node.map_or(class_node.start_position().row + 1, |node| {
18017            node.start_position().row + 1
18018        }),
18019        class_close_end,
18020        class_close_line,
18021        name,
18022    })
18023}
18024
18025/// Recognize the one malformed namespace-sentinel shape emitted for Abseil's
18026/// `namespace absl { ABSL_NAMESPACE_BEGIN namespace log_internal { ... }`.
18027///
18028/// The parser puts the namespace opener and the malformed function in one root
18029/// `ERROR` node.  This branch intentionally stays tied to that CST geometry:
18030/// the root's direct tokens must end in `namespace`, an identifier, and `{`;
18031/// the malformed function must begin with an all-caps type, then an ERROR whose
18032/// sole identifier is `namespace`, followed by the inner namespace identifier
18033/// and a compound body; and that body must contain a complete named class or a
18034/// structurally fragmented class prefix. A text reparse cannot prove any of
18035/// those ownership boundaries.
18036fn cpp_nested_namespace_sentinel<'tree>(
18037    node: Node<'tree>,
18038    source: &str,
18039    ancestry: &ParentIndex<'tree>,
18040) -> Option<CppNestedNamespaceSentinel<'tree>> {
18041    // The shape checks below are the admission proof: a `type_identifier`
18042    // sentinel followed by an `ERROR` holding the `namespace` keyword implies
18043    // parser damage on its own, and under the macro-declaration grammar the
18044    // `macro_modifier` wrapper and the keyword in the type field are each
18045    // admitted only where the parse would otherwise contain an error.
18046    let (function, mut namespace_components) = if node.kind() == "ERROR" {
18047        let mut cursor = node.walk();
18048        let functions = node
18049            .named_children(&mut cursor)
18050            .filter(|child| child.kind() == "function_definition")
18051            .collect::<Vec<_>>();
18052        let [function] = functions.as_slice() else {
18053            return None;
18054        };
18055        let mut cursor = node.walk();
18056        let children = node.children(&mut cursor).collect::<Vec<_>>();
18057        let function_index = children
18058            .iter()
18059            .position(|child| same_node(*child, *function))?;
18060        let [outer_keyword, outer_name, outer_open] =
18061            children.get(function_index.checked_sub(3)?..function_index)?
18062        else {
18063            return None;
18064        };
18065        if outer_keyword.kind() != "namespace"
18066            || !matches!(outer_name.kind(), "identifier" | "namespace_identifier")
18067            || outer_open.kind() != "{"
18068        {
18069            return None;
18070        }
18071        (
18072            *function,
18073            vec![canonical_cpp_qualified_component(*outer_name, source)?.name],
18074        )
18075    } else if node.kind() == "function_definition" {
18076        let declaration_list = node.parent()?;
18077        let namespace = declaration_list.parent()?;
18078        if declaration_list.kind() != "declaration_list"
18079            || namespace.kind() != "namespace_definition"
18080            || namespace.child_by_field_name("body") != Some(declaration_list)
18081        {
18082            return None;
18083        }
18084        (node, Vec::new())
18085    } else {
18086        return None;
18087    };
18088
18089    let mut cursor = function.walk();
18090    let named = function
18091        .named_children(&mut cursor)
18092        .filter(|child| child.kind() != "comment")
18093        .collect::<Vec<_>>();
18094    let [first_type, inner_error, inner_name, body] = named.as_slice() else {
18095        return None;
18096    };
18097    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
18098    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
18099    // the same token is wrapped in.
18100    let sentinel = if first_type.kind() == "type_identifier" {
18101        normalize_cpp_whitespace(node_text(*first_type, source))
18102    } else {
18103        let identifier = macro_modifier_identifier(*first_type)?;
18104        normalize_cpp_whitespace(node_text(identifier, source))
18105    };
18106    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18107        return None;
18108    }
18109    // The mis-lexed `namespace` keyword: an `ERROR` holding it under the older
18110    // grammar, or the function's bogus type field under the macro-declaration
18111    // grammar, where a keyword cannot be a type.
18112    let inner_keyword = if inner_error.kind() == "ERROR" && inner_error.named_child_count() == 1 {
18113        inner_error.named_child(0)?
18114    } else if inner_error.kind() == "type_identifier" {
18115        *inner_error
18116    } else {
18117        return None;
18118    };
18119    if direct_identifier_name(inner_keyword, source).as_deref() != Some("namespace") {
18120        return None;
18121    }
18122    if !matches!(inner_name.kind(), "identifier" | "namespace_identifier") {
18123        return None;
18124    }
18125    let inner_name = canonical_cpp_qualified_component(*inner_name, source)?.name;
18126    if inner_name.is_empty() || body.kind() != "compound_statement" {
18127        return None;
18128    }
18129    namespace_components.push(inner_name);
18130
18131    let mut cursor = body.walk();
18132    let has_complete_class = body.named_children(&mut cursor).any(|child| {
18133        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
18134            cpp_body_node(class_node).is_some()
18135                && class_like_name(class_node, source, ancestry)
18136                    .is_some_and(|name| !name.is_empty())
18137        })
18138    });
18139    if !has_complete_class
18140        && cpp_sentinel_fragmented_class_tail(function, *body, source, ancestry).is_none()
18141    {
18142        return None;
18143    }
18144
18145    Some(CppNestedNamespaceSentinel {
18146        function,
18147        body: *body,
18148        namespace_components,
18149    })
18150}
18151
18152/// Recognize a namespace-begin sentinel directly beneath the translation unit.
18153///
18154/// Tree-sitter reduces `BEGIN_NS namespace a::b { ... }` to a malformed
18155/// function whose type is the sentinel, whose declarator is the structured
18156/// qualified name `namespace::a::b`, and whose body contains the namespace
18157/// items. Declaration indexing already reparses this bounded region. The
18158/// inverse scanner retains the original tree, so recover the same namespace
18159/// components from the declarator fields for its lexical-scope metadata.
18160fn cpp_root_namespace_sentinel<'tree>(
18161    node: Node<'tree>,
18162    source: &str,
18163    ancestry: &ParentIndex<'tree>,
18164) -> Option<CppNestedNamespaceSentinel<'tree>> {
18165    if node.kind() != "function_definition" || node.parent()?.kind() != "translation_unit" {
18166        return None;
18167    }
18168    let body = node.child_by_field_name("body")?;
18169    let declarator = node.child_by_field_name("declarator")?;
18170    let mut cursor = node.walk();
18171    let named = node
18172        .named_children(&mut cursor)
18173        .filter(|child| child.kind() != "comment")
18174        .collect::<Vec<_>>();
18175    if body.kind() != "compound_statement" || !same_node(*named.last()?, body) {
18176        return None;
18177    }
18178    // The sentinel is the bogus return `type` (`type_identifier`), with the
18179    // literal `namespace` keyword leading the qualified declarator -- or,
18180    // under the macro-declaration grammar, a `macro_modifier`, with the
18181    // keyword occupying the type field (a keyword cannot be a type, which is
18182    // the same recovery evidence) and the namespace path as the declarator.
18183    let (sentinel, strip_namespace_keyword) = match named.as_slice() {
18184        [first, second, _] => {
18185            let first_type = node.child_by_field_name("type")?;
18186            if !same_node(*first, first_type)
18187                || first_type.kind() != "type_identifier"
18188                || !same_node(*second, declarator)
18189                || !node.has_error()
18190            {
18191                return None;
18192            }
18193            (
18194                normalize_cpp_whitespace(node_text(first_type, source)),
18195                true,
18196            )
18197        }
18198        [first, second, third, _] => {
18199            let type_node = node.child_by_field_name("type")?;
18200            let identifier = macro_modifier_identifier(*first)?;
18201            if !same_node(*second, type_node)
18202                || type_node.kind() != "type_identifier"
18203                || normalize_cpp_whitespace(node_text(type_node, source)) != "namespace"
18204                || !same_node(*third, declarator)
18205            {
18206                return None;
18207            }
18208            (
18209                normalize_cpp_whitespace(node_text(identifier, source)),
18210                false,
18211            )
18212        }
18213        _ => return None,
18214    };
18215    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
18216        return None;
18217    }
18218    if !matches!(
18219        declarator.kind(),
18220        "qualified_identifier" | "identifier" | "namespace_identifier"
18221    ) {
18222        return None;
18223    }
18224    let mut declarator_components = Vec::new();
18225    let mut valid_components = true;
18226    walk_named_tree_preorder(declarator, true, |component| {
18227        if !matches!(
18228            component.kind(),
18229            "identifier" | "namespace_identifier" | "type_identifier"
18230        ) {
18231            return WalkControl::Continue;
18232        }
18233        let Some(component) = canonical_cpp_qualified_component(component, source) else {
18234            valid_components = false;
18235            return WalkControl::Break;
18236        };
18237        declarator_components.push(component.name);
18238        WalkControl::SkipChildren
18239    });
18240    if strip_namespace_keyword {
18241        if !valid_components
18242            || declarator_components.first().map(String::as_str) != Some("namespace")
18243        {
18244            return None;
18245        }
18246        declarator_components.remove(0);
18247    }
18248    let namespace_components = declarator_components;
18249    if !valid_components
18250        || namespace_components.is_empty()
18251        || namespace_components
18252            .iter()
18253            .any(|component| component.is_empty() || cpp_export_macro_token(component))
18254    {
18255        return None;
18256    }
18257
18258    let mut cursor = body.walk();
18259    let has_complete_class = body.named_children(&mut cursor).any(|child| {
18260        cpp_sentinel_body_class_candidate(child).is_some_and(|(class_node, _)| {
18261            cpp_body_node(class_node).is_some()
18262                && class_like_name(class_node, source, ancestry)
18263                    .is_some_and(|name| !name.is_empty())
18264        })
18265    });
18266    if !has_complete_class
18267        && cpp_sentinel_fragmented_class_tail(node, body, source, ancestry).is_none()
18268    {
18269        return None;
18270    }
18271
18272    Some(CppNestedNamespaceSentinel {
18273        function: node,
18274        body,
18275        namespace_components,
18276    })
18277}
18278
18279/// Recover one fragmented class tail that tree-sitter leaves as siblings of the
18280/// malformed namespace-sentinel function.  The recovery is deliberately
18281/// structural: the class must be a direct body item, its own class node must be
18282/// erroneous and end before a unique anonymous `}` in the enclosing
18283/// declaration-list, and that namespace's next sibling must be a standalone
18284/// `;`.  The complete interior must pass the existing member-shaped reparse
18285/// gate. This avoids source brace scans and does not borrow a close from an
18286/// unrelated later declaration.
18287fn cpp_sentinel_fragmented_class_tail<'tree>(
18288    function: Node<'tree>,
18289    body: Node<'tree>,
18290    source: &str,
18291    ancestry: &ParentIndex<'tree>,
18292) -> Option<CppSentinelFragmentedClassTail<'tree>> {
18293    let mut cursor = body.walk();
18294    let candidates = body
18295        .named_children(&mut cursor)
18296        .filter_map(|child| {
18297            if let Some((class_node, template_node)) = cpp_sentinel_body_class_candidate(child) {
18298                let class_body = cpp_body_node(class_node)?;
18299                if !class_node.has_error() {
18300                    return None;
18301                }
18302                let name = class_like_name(class_node, source, ancestry)?;
18303                let raw_supertypes =
18304                    matches!(class_node.kind(), "class_specifier" | "struct_specifier")
18305                        .then(|| extract_cpp_supertypes(class_node, source));
18306                return Some((
18307                    class_node,
18308                    template_node,
18309                    name,
18310                    class_body,
18311                    class_body.start_byte().checked_add(1)?,
18312                    raw_supertypes,
18313                    class_body,
18314                ));
18315            }
18316            let prefix = cpp_sentinel_fragmented_class_error_prefix(child, source)?;
18317            Some((
18318                child,
18319                None,
18320                prefix.name,
18321                prefix.open,
18322                prefix.open.end_byte(),
18323                prefix.raw_supertypes,
18324                child,
18325            ))
18326        })
18327        .collect::<Vec<_>>();
18328    let [
18329        (
18330            class_node,
18331            template_node,
18332            name,
18333            class_body,
18334            reparse_start,
18335            raw_supertypes,
18336            member_container,
18337        ),
18338    ] = candidates.as_slice()
18339    else {
18340        return None;
18341    };
18342    if name.is_empty() {
18343        return None;
18344    }
18345
18346    let (close, semicolon) =
18347        cpp_sentinel_fragment_boundary(function, *class_node, *class_body, source)?;
18348
18349    let reparse_end = close.start_byte();
18350    if *reparse_start >= reparse_end {
18351        return None;
18352    }
18353    let tree = cpp_reparse_region_items(source, *reparse_start, reparse_end)?;
18354    if !cpp_reparsed_members_are_indexable_for_class(tree.root_node(), source, Some(name)) {
18355        return None;
18356    }
18357    let class_range = Range {
18358        start_byte: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
18359        end_byte: semicolon.end_byte(),
18360        start_line: template_node.map_or(class_node.start_position().row, |node| {
18361            node.start_position().row
18362        }) + 1,
18363        end_line: semicolon.end_position().row + 1,
18364    };
18365    Some(CppSentinelFragmentedClassTail {
18366        class_node: *class_node,
18367        template_node: *template_node,
18368        name: name.clone(),
18369        raw_supertypes: raw_supertypes.clone(),
18370        fragmented: FragmentedExportBody {
18371            reparse_start: *reparse_start,
18372            reparse_end,
18373            class_range,
18374        },
18375        consumed_start: template_node.map_or(class_node.start_byte(), |node| node.start_byte()),
18376        member_container: *member_container,
18377    })
18378}
18379
18380/// The alias declarations a fragmented sentinel class still holds as
18381/// structured direct members in the original tree (#6902).
18382///
18383/// The member reparse cannot keep them: after an access label it reads
18384/// `using value_type = int;` in statement context, as a `using` variable
18385/// named `value_type`, and loses the target. The original node keeps the
18386/// alias kind, the target type, the exact source range, and the template and
18387/// preprocessor context. Ownership is proven by position alone: a direct
18388/// child of the class's member container, inside the interior the member
18389/// reparse covers, or inside a preprocessor conditional that is one. An alias
18390/// nested anywhere else is not admitted here.
18391fn cpp_sentinel_fragmented_class_aliases<'tree>(
18392    tail: &CppSentinelFragmentedClassTail<'tree>,
18393) -> Vec<Node<'tree>> {
18394    let mut aliases = Vec::new();
18395    let mut containers = vec![tail.member_container];
18396    while let Some(container) = containers.pop() {
18397        let mut cursor = container.walk();
18398        for child in container.named_children(&mut cursor) {
18399            if child.start_byte() < tail.fragmented.reparse_start
18400                || child.end_byte() > tail.fragmented.reparse_end
18401            {
18402                continue;
18403            }
18404            match child.kind() {
18405                // A conditional member keeps its owner; its guard comes from
18406                // this same original ancestry.
18407                "preproc_if" | "preproc_ifdef" | "preproc_else" | "preproc_elif"
18408                | "preproc_elifdef" => containers.push(child),
18409                "alias_declaration" if !child.has_error() => aliases.push(child),
18410                "template_declaration"
18411                    if !child.has_error()
18412                        && child
18413                            .named_children(&mut child.walk())
18414                            .any(|declaration| declaration.kind() == "alias_declaration") =>
18415                {
18416                    aliases.push(child)
18417                }
18418                _ => {}
18419            }
18420        }
18421    }
18422    aliases
18423}
18424
18425/// Recover the class and out-of-line owner scopes from every malformed
18426/// namespace-sentinel region in `root`.
18427///
18428/// This is the shared structural counterpart to
18429/// [`CppDeclarationVisitor::visit_nested_namespace_sentinel`].  It intentionally
18430/// reuses the visitor's sentinel/class admission predicates instead of parsing
18431/// source text a second time.  The returned values own only ranges and names, so
18432/// they can be retained by an inverted usage scan after the tree borrow ends.
18433pub fn cpp_sentinel_recovered_classes(
18434    root: Node<'_>,
18435    source: &str,
18436) -> Vec<CppSentinelRecoveredClass> {
18437    // The macro-declaration grammar absorbs the sentinel damage into structured
18438    // nodes and can leave the whole tree clean: `macro_modifier` and its kin are
18439    // admitted only where the older grammar would have reported an error, so a
18440    // clean tree carrying them holds the same recovery evidence. A tree with
18441    // neither errors nor macro-declaration nodes has no sentinel region.
18442    if !root.has_error()
18443        && !subtree_contains(root, |node| {
18444            matches!(
18445                node.kind(),
18446                "macro_modifier" | "macro_modifier_declarator" | "macro_type_specifier"
18447            )
18448        })
18449    {
18450        return Vec::new();
18451    }
18452    // This scan owns its walk of `root`, so it owns the parent index that walk
18453    // asks its ancestor questions through. Built after the error gate: a clean
18454    // tree without macro-declaration evidence returns without paying for one.
18455    let ancestry = ParentIndex::new(root);
18456    let mut recovered_classes: Vec<CppSentinelRecoveredClass> = Vec::new();
18457    let mut stack = vec![root];
18458    while let Some(current) = stack.pop() {
18459        if let Some(recovered) = cpp_nested_namespace_sentinel(current, source, &ancestry)
18460            .or_else(|| cpp_root_namespace_sentinel(current, source, &ancestry))
18461        {
18462            let namespace_components = cpp_sentinel_recovered_namespace_components(
18463                recovered.function,
18464                &recovered.namespace_components,
18465                source,
18466            );
18467            let fragmented = cpp_sentinel_fragmented_class_tail(
18468                recovered.function,
18469                recovered.body,
18470                source,
18471                &ancestry,
18472            );
18473            let mut class_candidates = Vec::new();
18474            let mut cursor = recovered.body.walk();
18475            for (class_node, template_node) in recovered
18476                .body
18477                .named_children(&mut cursor)
18478                .filter_map(cpp_sentinel_body_class_candidate)
18479            {
18480                let Some(name) = class_like_name(class_node, source, &ancestry) else {
18481                    continue;
18482                };
18483                if name.is_empty() {
18484                    continue;
18485                }
18486                let is_fragmented = fragmented
18487                    .as_ref()
18488                    .is_some_and(|tail| same_node(tail.class_node, class_node));
18489                if !is_fragmented && cpp_complete_class_body_close(class_node).is_none() {
18490                    continue;
18491                }
18492                let class_range = if is_fragmented {
18493                    fragmented
18494                        .as_ref()
18495                        .map(|tail| tail.fragmented.class_range)
18496                        .expect("fragmented class range is present when class matches")
18497                } else {
18498                    cpp_declaration_range(template_node.unwrap_or(class_node))
18499                };
18500                class_candidates.push((class_range, name));
18501            }
18502            if let Some(fragmented) = fragmented
18503                .as_ref()
18504                .filter(|tail| tail.class_node.kind() == "ERROR")
18505            {
18506                class_candidates.push((fragmented.fragmented.class_range, fragmented.name.clone()));
18507            }
18508
18509            let mut owner_ranges =
18510                cpp_sentinel_recovered_owner_ranges(recovered.body, &namespace_components, source);
18511            cpp_sentinel_extend_unique_owner_ranges(
18512                &mut owner_ranges,
18513                cpp_sentinel_recovered_sibling_owner_ranges(
18514                    recovered.function,
18515                    &namespace_components,
18516                    source,
18517                ),
18518            );
18519            for (class_range, name) in class_candidates {
18520                push_cpp_sentinel_recovered_class(
18521                    &mut recovered_classes,
18522                    cpp_declaration_range(recovered.body),
18523                    &namespace_components,
18524                    class_range,
18525                    name,
18526                    &owner_ranges,
18527                );
18528            }
18529
18530            if let Some(declaration_list) = recovered
18531                .function
18532                .parent()
18533                .filter(|parent| parent.kind() == "declaration_list")
18534            {
18535                let outer_namespace =
18536                    cpp_sentinel_recovered_namespace_components(recovered.function, &[], source);
18537                push_cpp_sentinel_sibling_classes(
18538                    &mut recovered_classes,
18539                    declaration_list,
18540                    recovered.function,
18541                    &outer_namespace,
18542                    source,
18543                    &ancestry,
18544                );
18545            }
18546        } else if let Some(region) =
18547            cpp_sentinel_macro_body_class_region(current, source, &ancestry)
18548        {
18549            let namespace_components = cpp_sentinel_recovered_namespace_components(
18550                current,
18551                &region.namespace_components,
18552                source,
18553            );
18554            let owner_container = current
18555                .parent()
18556                .filter(|parent| parent.kind() == "declaration_list")
18557                .unwrap_or(current);
18558            let owner_ranges =
18559                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18560            push_cpp_sentinel_recovered_class(
18561                &mut recovered_classes,
18562                cpp_declaration_range(owner_container),
18563                &namespace_components,
18564                Range {
18565                    start_byte: region.class_start,
18566                    end_byte: region.class_close_end,
18567                    start_line: region.class_start_line,
18568                    end_line: region.class_close_line,
18569                },
18570                region.name,
18571                &owner_ranges,
18572            );
18573        } else if let Some(region) = cpp_sentinel_macro_class_region(current, source) {
18574            // A generic sentinel-prefixed class can be reduced as a malformed
18575            // function/ERROR without the explicit `namespace X` token pair.
18576            // Reuse the declaration visitor's bounded reparse and retain only
18577            // the recovered class identity/range here.
18578            let (reparse_start, class_start, _body_start, _close_start, close_end, _close_line) =
18579                region;
18580            let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
18581                continue;
18582            };
18583            let root = tree.root_node();
18584            let template_node = cpp_sentinel_reparsed_leading_template(root);
18585            // A region reparse is its own tree and needs its own parent index.
18586            let reparsed_ancestry = ParentIndex::new(root);
18587            let Some(reparsed_class) =
18588                cpp_sentinel_reparsed_class(root, template_node, source, &reparsed_ancestry)
18589            else {
18590                continue;
18591            };
18592            let class_node = reparsed_class.declaration_node;
18593            let name = reparsed_class.name;
18594            let namespace_components =
18595                cpp_sentinel_recovered_namespace_components(current, &[], source);
18596            let owner_container = current
18597                .parent()
18598                .filter(|parent| parent.kind() == "declaration_list")
18599                .unwrap_or(current);
18600            let mut owner_ranges =
18601                cpp_sentinel_recovered_owner_ranges(owner_container, &namespace_components, source);
18602            cpp_sentinel_extend_unique_owner_ranges(
18603                &mut owner_ranges,
18604                cpp_sentinel_recovered_sibling_owner_ranges(current, &namespace_components, source),
18605            );
18606            push_cpp_sentinel_recovered_class(
18607                &mut recovered_classes,
18608                cpp_declaration_range(owner_container),
18609                &namespace_components,
18610                Range {
18611                    start_byte: class_start,
18612                    end_byte: close_end,
18613                    start_line: class_node.start_position().row + 1,
18614                    end_line: class_node.end_position().row + 1,
18615                },
18616                name,
18617                &owner_ranges,
18618            );
18619            if owner_container.kind() == "declaration_list" {
18620                push_cpp_sentinel_sibling_classes(
18621                    &mut recovered_classes,
18622                    owner_container,
18623                    current,
18624                    &namespace_components,
18625                    source,
18626                    &ancestry,
18627                );
18628            }
18629        }
18630
18631        let mut cursor = current.walk();
18632        stack.extend(current.named_children(&mut cursor));
18633    }
18634    // A shallower sentinel can expose nested classes as apparent namespace
18635    // siblings even after a deeper sentinel proves that a containing class
18636    // owns their ranges. Drop those shadow descriptors; scope recovery starts
18637    // from the proven containing class and appends parser-visible class
18638    // ancestors, preserving the full `Outer::Inner` chain.
18639    let shadowed = recovered_classes
18640        .iter()
18641        .map(|candidate| {
18642            recovered_classes.iter().any(|container| {
18643                container.class_range.start_byte <= candidate.class_range.start_byte
18644                    && container.class_range.end_byte >= candidate.class_range.end_byte
18645                    && container.class_range != candidate.class_range
18646                    && container.namespace_scope_components.len()
18647                        > candidate.namespace_scope_components.len()
18648                    && container
18649                        .namespace_scope_components
18650                        .starts_with(&candidate.namespace_scope_components)
18651            })
18652        })
18653        .collect::<Vec<_>>();
18654    let mut index = 0usize;
18655    recovered_classes.retain(|_| {
18656        let keep = !shadowed[index];
18657        index += 1;
18658        keep
18659    });
18660    recovered_classes
18661}
18662
18663/// A flat sentinel can swallow the first class while leaving later classes and
18664/// their out-of-line definitions as ordinary declaration-list siblings.  Once
18665/// the malformed class proves the sentinel envelope, retain those structurally
18666/// complete sibling classes under the same surviving namespace so every member
18667/// owner in the region uses one recovery contract.
18668fn push_cpp_sentinel_sibling_classes<'tree>(
18669    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18670    declaration_list: Node<'tree>,
18671    sentinel_node: Node<'tree>,
18672    namespace_components: &[String],
18673    source: &str,
18674    ancestry: &ParentIndex<'tree>,
18675) {
18676    let owner_ranges =
18677        cpp_sentinel_recovered_owner_ranges(declaration_list, namespace_components, source);
18678    let namespace_range = cpp_declaration_range(declaration_list);
18679    let mut cursor = declaration_list.walk();
18680    for (class_node, template_node) in declaration_list
18681        .named_children(&mut cursor)
18682        .filter(|child| !same_node(*child, sentinel_node))
18683        .filter_map(cpp_sentinel_body_class_candidate)
18684    {
18685        let Some(name) = class_like_name(class_node, source, ancestry) else {
18686            continue;
18687        };
18688        if name.is_empty() || cpp_complete_class_body_close(class_node).is_none() {
18689            continue;
18690        }
18691        push_cpp_sentinel_recovered_class(
18692            recovered_classes,
18693            namespace_range,
18694            namespace_components,
18695            cpp_declaration_range(template_node.unwrap_or(class_node)),
18696            name,
18697            &owner_ranges,
18698        );
18699    }
18700}
18701
18702fn push_cpp_sentinel_recovered_class(
18703    recovered_classes: &mut Vec<CppSentinelRecoveredClass>,
18704    namespace_range: Range,
18705    namespace_components: &[String],
18706    class_range: Range,
18707    name: String,
18708    owner_ranges: &[CppSentinelRecoveredOwner],
18709) {
18710    let mut scope_components = namespace_components.to_vec();
18711    scope_components.push(name);
18712    let owner_ranges = owner_ranges
18713        .iter()
18714        .filter(|owner| owner.scope_components.starts_with(&scope_components))
18715        .cloned()
18716        .collect::<Vec<_>>();
18717    if recovered_classes.iter().any(|existing| {
18718        existing.class_range == class_range && existing.scope_components == scope_components
18719    }) {
18720        return;
18721    }
18722    recovered_classes.push(CppSentinelRecoveredClass {
18723        namespace_range,
18724        namespace_scope_components: namespace_components.to_vec(),
18725        class_range,
18726        scope_components,
18727        owner_ranges,
18728    });
18729}
18730
18731fn cpp_sentinel_recovered_namespace_components(
18732    function: Node<'_>,
18733    recovered_components: &[String],
18734    source: &str,
18735) -> Vec<String> {
18736    let mut ancestor_components = Vec::new();
18737    let mut ancestor = function.parent();
18738    while let Some(current) = ancestor {
18739        if current.kind() == "namespace_definition"
18740            && let Some(name_node) = current.child_by_field_name("name")
18741            && let Some(components) = cpp_name_components(name_node, source)
18742        {
18743            ancestor_components.push(
18744                components
18745                    .into_iter()
18746                    .map(|component| component.name)
18747                    .collect::<Vec<_>>(),
18748            );
18749        }
18750        ancestor = current.parent();
18751    }
18752    ancestor_components.reverse();
18753    let mut ancestors = ancestor_components
18754        .into_iter()
18755        .flatten()
18756        .collect::<Vec<_>>();
18757
18758    let overlap = (0..=ancestors.len().min(recovered_components.len()))
18759        .rev()
18760        .find(|length| {
18761            ancestors[ancestors.len().saturating_sub(*length)..] == recovered_components[..*length]
18762        })
18763        .unwrap_or(0);
18764    ancestors.extend(recovered_components.iter().skip(overlap).cloned());
18765    ancestors
18766}
18767
18768fn cpp_sentinel_recovered_owner_ranges(
18769    body: Node<'_>,
18770    namespace_components: &[String],
18771    source: &str,
18772) -> Vec<CppSentinelRecoveredOwner> {
18773    let mut owners = Vec::new();
18774    walk_named_tree_preorder(body, true, |node| {
18775        cpp_sentinel_collect_owner_range(node, namespace_components, source, &mut owners)
18776    });
18777    owners
18778}
18779
18780fn cpp_sentinel_collect_owner_range(
18781    node: Node<'_>,
18782    namespace_components: &[String],
18783    source: &str,
18784    owners: &mut Vec<CppSentinelRecoveredOwner>,
18785) -> WalkControl {
18786    if node.kind() != "function_definition" {
18787        return WalkControl::Continue;
18788    }
18789    let Some(function_declarator) = extract_function_declarator(node) else {
18790        return WalkControl::Continue;
18791    };
18792    let Some(name_node) = cpp_function_declarator_name_node(function_declarator) else {
18793        return WalkControl::Continue;
18794    };
18795    let Some(mut components) = cpp_name_components(name_node, source) else {
18796        return WalkControl::Continue;
18797    };
18798    if components.len() <= 1 {
18799        return WalkControl::Continue;
18800    }
18801    components.pop();
18802    let mut owner_components = components
18803        .into_iter()
18804        .map(|component| component.name)
18805        .collect::<Vec<_>>();
18806    let overlap = (0..=namespace_components.len().min(owner_components.len()))
18807        .rev()
18808        .find(|length| {
18809            owner_components[..*length]
18810                == namespace_components[namespace_components.len().saturating_sub(*length)..]
18811        })
18812        .unwrap_or(0);
18813    let mut scope_components = namespace_components.to_vec();
18814    scope_components.extend(owner_components.drain(overlap..));
18815    if scope_components.len() <= namespace_components.len() {
18816        return WalkControl::Continue;
18817    }
18818    let range = cpp_declaration_range(node);
18819    if !owners.iter().any(|existing: &CppSentinelRecoveredOwner| {
18820        existing.range == range && existing.scope_components == scope_components
18821    }) {
18822        owners.push(CppSentinelRecoveredOwner {
18823            range,
18824            owner_name_start_byte: name_node.start_byte(),
18825            namespace_component_count: namespace_components.len(),
18826            scope_components,
18827        });
18828    }
18829    WalkControl::Continue
18830}
18831
18832fn cpp_sentinel_extend_unique_owner_ranges(
18833    owners: &mut Vec<CppSentinelRecoveredOwner>,
18834    additional: Vec<CppSentinelRecoveredOwner>,
18835) {
18836    for owner in additional {
18837        if !owners.iter().any(|existing| {
18838            existing.range == owner.range && existing.scope_components == owner.scope_components
18839        }) {
18840            owners.push(owner);
18841        }
18842    }
18843}
18844
18845fn cpp_sentinel_namespace_end(node: Node<'_>, source: &str) -> bool {
18846    if node.kind() != "ERROR" || node.named_child_count() != 1 {
18847        return false;
18848    }
18849    let Some(end_name) = node.named_child(0) else {
18850        return false;
18851    };
18852    if direct_identifier_name(end_name, source).as_deref() != Some("ABSL_NAMESPACE_END") {
18853        return false;
18854    }
18855    let mut cursor = node.walk();
18856    node.children(&mut cursor)
18857        .any(|child| child.kind() == "}" && !child.is_named() && !child.is_missing())
18858}
18859
18860/// Collect owner definitions that the malformed sentinel left as later
18861/// declaration-list siblings. Parser-visible namespace siblings are a hard
18862/// boundary: their declarations must keep their own lexical namespace.
18863fn cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18864    parent: Node<'_>,
18865    sentinel_node: Node<'_>,
18866    namespace_components: &[String],
18867    source: &str,
18868) -> Vec<CppSentinelRecoveredOwner> {
18869    let mut owners = Vec::new();
18870    let mut after_sentinel = false;
18871    let mut cursor = parent.walk();
18872    for child in parent.named_children(&mut cursor) {
18873        if !after_sentinel {
18874            if same_node(child, sentinel_node) {
18875                after_sentinel = true;
18876            }
18877            continue;
18878        }
18879        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18880    }
18881    owners
18882}
18883
18884/// Collect owner definitions after a malformed namespace, stopping only at
18885/// its structural `ABSL_NAMESPACE_END` error marker. Without that marker the
18886/// enclosing container is not trusted to belong to the recovered namespace.
18887fn cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18888    parent: Node<'_>,
18889    sentinel_node: Node<'_>,
18890    namespace_components: &[String],
18891    source: &str,
18892) -> Option<Vec<CppSentinelRecoveredOwner>> {
18893    let mut owners = Vec::new();
18894    let mut after_namespace = false;
18895    let mut cursor = parent.walk();
18896    for child in parent.named_children(&mut cursor) {
18897        if !after_namespace {
18898            if same_node(child, sentinel_node) {
18899                after_namespace = true;
18900            }
18901            continue;
18902        }
18903        if cpp_sentinel_namespace_end(child, source) {
18904            return Some(owners);
18905        }
18906        cpp_sentinel_append_owner_ranges(child, namespace_components, source, &mut owners);
18907    }
18908    None
18909}
18910
18911fn cpp_sentinel_append_owner_ranges(
18912    sibling: Node<'_>,
18913    namespace_components: &[String],
18914    source: &str,
18915    owners: &mut Vec<CppSentinelRecoveredOwner>,
18916) {
18917    walk_named_tree_preorder(sibling, true, |node| {
18918        if node.kind() == "namespace_definition" {
18919            return WalkControl::SkipChildren;
18920        }
18921        cpp_sentinel_collect_owner_range(node, namespace_components, source, owners)
18922    });
18923}
18924
18925fn cpp_sentinel_recovered_sibling_owner_ranges(
18926    sentinel_node: Node<'_>,
18927    namespace_components: &[String],
18928    source: &str,
18929) -> Vec<CppSentinelRecoveredOwner> {
18930    let Some(declaration_list) = sentinel_node
18931        .parent()
18932        .filter(|parent| parent.kind() == "declaration_list")
18933    else {
18934        return Vec::new();
18935    };
18936    let mut owners = cpp_sentinel_recovered_owner_ranges_after_declaration_siblings(
18937        declaration_list,
18938        sentinel_node,
18939        namespace_components,
18940        source,
18941    );
18942
18943    let Some(namespace) = declaration_list
18944        .parent()
18945        .filter(|parent| parent.kind() == "namespace_definition")
18946    else {
18947        return owners;
18948    };
18949    let Some(outer_parent) = namespace.parent() else {
18950        return owners;
18951    };
18952    if let Some(additional) = cpp_sentinel_recovered_owner_ranges_after_namespace_siblings(
18953        outer_parent,
18954        namespace,
18955        namespace_components,
18956        source,
18957    ) {
18958        cpp_sentinel_extend_unique_owner_ranges(&mut owners, additional);
18959    }
18960    owners
18961}
18962
18963fn cpp_function_declarator_name_node(function_declarator: Node<'_>) -> Option<Node<'_>> {
18964    let mut current = function_declarator.child_by_field_name("declarator")?;
18965    loop {
18966        if let Some(name) = macro_decorated_unqualified_name(current) {
18967            current = name;
18968            continue;
18969        }
18970        if matches!(
18971            current.kind(),
18972            "qualified_identifier"
18973                | "scoped_identifier"
18974                | "scoped_type_identifier"
18975                | "identifier"
18976                | "field_identifier"
18977                | "operator_name"
18978                | "destructor_name"
18979                | "literal_operator_name"
18980        ) {
18981            return Some(current);
18982        }
18983        current = current
18984            .child_by_field_name("declarator")
18985            .or_else(|| current.child_by_field_name("name"))
18986            .or_else(|| last_named_child(current))?;
18987    }
18988}
18989
18990/// A `qualified_identifier` the grammar produced for `MACRO Name` with no
18991/// `::` between the halves. The scope is an attribute-like macro token, not
18992/// a namespace; `Name` is the declared name.
18993///
18994/// `explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(__m256i v)` is the witness (#2552):
18995/// tree-sitter joins the attribute macro and the constructor name into one
18996/// `qualified_identifier` and marks the separator it had to invent as MISSING.
18997/// That flag is the grammar's own record that no separator is in the source,
18998/// so read it instead of looking for `::` in the node text. A genuine
18999/// `Outer::Inner` keeps a present separator and is declined here, and so is
19000/// the explicit-global `::name`, whose `::` is present and whose scope is
19001/// absent.
19002fn macro_decorated_unqualified_name(node: Node<'_>) -> Option<Node<'_>> {
19003    if node.kind() != "qualified_identifier" || node.child_by_field_name("scope").is_none() {
19004        return None;
19005    }
19006    let mut cursor = node.walk();
19007    if node
19008        .children(&mut cursor)
19009        .any(|child| child.kind() == "::" && !child.is_missing())
19010    {
19011        return None;
19012    }
19013    node.child_by_field_name("name")
19014}
19015
19016fn cpp_name_components(node: Node<'_>, source: &str) -> Option<Vec<CppQualifiedNameComponent>> {
19017    enum Component<'tree> {
19018        Node(Node<'tree>),
19019        Name(Node<'tree>),
19020    }
19021    let mut stack = vec![Component::Node(node)];
19022    let mut components = Vec::new();
19023    while let Some(component) = stack.pop() {
19024        let node = match component {
19025            Component::Name(node) => {
19026                components.push(canonical_cpp_qualified_component(
19027                    node.child_by_field_name("name")?,
19028                    source,
19029                )?);
19030                continue;
19031            }
19032            Component::Node(node) => node,
19033        };
19034        if let Some(name) = macro_decorated_unqualified_name(node) {
19035            stack.push(Component::Node(name));
19036            continue;
19037        }
19038        match node.kind() {
19039            "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier" => {
19040                stack.push(Component::Name(node));
19041                if let Some(scope) = node.child_by_field_name("scope") {
19042                    stack.push(Component::Node(scope));
19043                }
19044            }
19045            _ => components.push(canonical_cpp_qualified_component(node, source)?),
19046        }
19047    }
19048    Some(components)
19049}
19050
19051fn cpp_sentinel_fragment_boundary<'tree>(
19052    function: Node<'tree>,
19053    class_node: Node<'tree>,
19054    class_body: Node<'tree>,
19055    source: &str,
19056) -> Option<(Node<'tree>, Node<'tree>)> {
19057    let declaration_list = function.parent()?;
19058    if function.kind() != "function_definition" || declaration_list.kind() != "declaration_list" {
19059        return None;
19060    }
19061    let namespace = declaration_list.parent()?;
19062    if namespace.kind() != "namespace_definition"
19063        || namespace.child_by_field_name("body") != Some(declaration_list)
19064    {
19065        return None;
19066    }
19067    let mut cursor = declaration_list.walk();
19068    let closes = declaration_list
19069        .children(&mut cursor)
19070        .filter(|child| {
19071            !child.is_named()
19072                && child.kind() == "}"
19073                && child.start_byte() >= function.end_byte()
19074                && child.start_byte() > class_node.end_byte()
19075                && child.start_byte() > class_body.start_byte()
19076        })
19077        .collect::<Vec<_>>();
19078    let [close] = closes.as_slice() else {
19079        return None;
19080    };
19081    let semicolon = namespace.next_named_sibling()?;
19082    if !cpp_is_stray_semicolon(semicolon, source)
19083        || close.end_byte() != namespace.end_byte()
19084        || semicolon.start_byte() < namespace.end_byte()
19085    {
19086        return None;
19087    }
19088    Some((*close, semicolon))
19089}
19090
19091/// Detect the bogus declaration/function tree that tree-sitter recovers for a
19092/// region prefixed by an object-like macro sentinel the parser cannot see
19093/// (issue #941), and return the byte range `[start, end)` of the swallowed
19094/// declaration interior to reparse.
19095///
19096/// The measured shape (`BEGIN_NS\nnamespace X { struct A { void m(); }; }`) is a
19097/// `function_definition` whose first non-comment named child is the sentinel
19098/// mis-read as the return `type` (a bare all-caps `type_identifier`), followed
19099/// by the mis-lexed item keyword, an `ERROR`, and a `compound_statement` holding
19100/// the real items.
19101/// `start` is the end of the sentinel identifier -- everything after it is the
19102/// genuine source. `end` is the node's end, extended across any trailing empty
19103/// `;` statement the mis-parse displaced past the node (the class/struct closing
19104/// semicolon), so the reparse sees a complete, brace-balanced item.
19105///
19106/// False-positive guards: the candidate must itself carry an `ERROR`/`MISSING`
19107/// node (`has_error`), or -- under the macro-declaration grammar, which parses
19108/// the sentinel region without recovery -- carry the sentinel as a
19109/// `macro_modifier`, a production admitted only where the parse would
19110/// otherwise contain an error. Unknown annotation/export macros can make a
19111/// real callable error-recovered even though tree-sitter still preserves its
19112/// declarator, so a preserved callable is admitted only when a displaced class
19113/// keyword precedes that declarator. The clean-reparse-to-items gate in
19114/// `cpp_reparsed_items_are_indexable` is the final arbiter.
19115/// Return the reparse start and, when present, the structurally recovered class
19116/// keyword for a malformed sentinel-prefixed node.  The class keyword is kept
19117/// separately from the reparse start because an opaque template-declaration
19118/// macro may precede it.
19119fn cpp_sentinel_macro_parts(node: Node<'_>, source: &str) -> Option<(usize, Option<usize>)> {
19120    if !matches!(node.kind(), "function_definition" | "declaration" | "ERROR") {
19121        return None;
19122    }
19123    // OpenJDK's generated `EXPORT void f(struct Value value) { ... }` functions
19124    // retain a valid function declarator despite the unknown export macro making
19125    // the outer node erroneous. Remember that declarator for the ordering gate
19126    // below: a `struct` parameter lies inside it, while a sentinel-swallowed
19127    // class keyword precedes a spurious callable assembled from a later member.
19128    let mut declarator_cursor = node.walk();
19129    let preserved_callable = node
19130        .children_by_field_name("declarator", &mut declarator_cursor)
19131        .find_map(extract_function_declarator);
19132    // Leading documentation comments are attached to the malformed
19133    // `function_definition` as named children.  They are not part of the
19134    // sentinel prefix, so select the first non-comment child structurally
19135    // rather than requiring the sentinel to be child zero.  This is the shape
19136    // emitted for nlohmann/json's `basic_json`: its class documentation comment
19137    // precedes `NLOHMANN_BASIC_JSON_TPL_DECLARATION`, and the malformed node's
19138    // envelope otherwise ends at the first nested union.
19139    let mut cursor = node.walk();
19140    let first = node
19141        .named_children(&mut cursor)
19142        .find(|child| child.kind() != "comment")?;
19143    // The sentinel is a bare all-caps `type_identifier` mis-read as the return
19144    // type, or -- under the macro-declaration grammar -- the `macro_modifier`
19145    // the same token is wrapped in. The wrapper is itself the recovery
19146    // evidence the older grammar's `has_error` carried, so a clean envelope is
19147    // admitted only in that form. The macro-declaration grammar can also keep
19148    // the sentinel as the `type` and absorb the displaced head that follows it
19149    // (`BEGIN_NS TEMPLATE_DECLARATION class Box { ... }`) into a
19150    // `macro_modifier_declarator` chain, which carries the same evidence.
19151    let (first_end, sentinel) = if first.kind() == "type_identifier" {
19152        if !node.has_error()
19153            && node
19154                .child_by_field_name("declarator")
19155                .is_none_or(|declarator| declarator.kind() != "macro_modifier_declarator")
19156        {
19157            return None;
19158        }
19159        (
19160            first.end_byte(),
19161            normalize_cpp_whitespace(node_text(first, source)),
19162        )
19163    } else {
19164        let identifier = macro_modifier_identifier(first)?;
19165        (
19166            first.end_byte(),
19167            normalize_cpp_whitespace(node_text(identifier, source)),
19168        )
19169    };
19170    if sentinel.is_empty() || !cpp_export_macro_token(&sentinel) {
19171        return None;
19172    }
19173    // Consecutive begin/end sentinels stack: `END_NS BEGIN_NS namespace two {...}`
19174    // makes the trailing sentinel of one region and the leading sentinel of the
19175    // next both land as bare macro-token identifiers ahead of the real content.
19176    // Advance past every leading macro-token identifier so the reparse begins at
19177    // genuine source rather than another sentinel that would re-form the bogus
19178    // shape and fail the reparse gate.
19179    let mut start = first_end;
19180    let mut after_first = false;
19181    let mut cursor = node.walk();
19182    for child in node.named_children(&mut cursor) {
19183        if !after_first {
19184            if same_node(child, first) {
19185                after_first = true;
19186            }
19187            continue;
19188        }
19189        let macro_token = if matches!(child.kind(), "identifier" | "type_identifier") {
19190            Some(child)
19191        } else {
19192            macro_modifier_identifier(child)
19193        };
19194        if macro_token.is_some_and(|token| {
19195            cpp_export_macro_token(&normalize_cpp_whitespace(node_text(token, source)))
19196        }) {
19197            start = child.end_byte();
19198        } else {
19199            break;
19200        }
19201    }
19202    // An additional opaque template-declaration macro before a class can be
19203    // folded into the bogus function's qualified declarator.  In that shape
19204    // the macro is not a direct sibling we can skip above; tree-sitter exposes
19205    // the displaced `class`/`struct` keyword as an identifier inside an ERROR.
19206    // Reparse from that keyword (or a real preceding `template` keyword) so the
19207    // ordinary class visitor owns the body.  Only inspect the declarator prefix:
19208    // a class nested in a genuine sentinel-wrapped namespace lies after the
19209    // body opening and must not change the established region start.
19210    let prefix_end = cpp_body_node(node).map_or(node.end_byte(), |body| body.start_byte());
19211    let mut class_start = None;
19212    let mut template_start = None;
19213    let mut stack = vec![node];
19214    while let Some(current) = stack.pop() {
19215        if current.start_byte() >= prefix_end {
19216            continue;
19217        }
19218        if matches!(
19219            current.kind(),
19220            "identifier" | "type_identifier" | "class" | "struct" | "union" | "enum" | "template"
19221        ) {
19222            match normalize_cpp_whitespace(node_text(current, source)).as_str() {
19223                "class" | "struct" | "union" | "enum" => {
19224                    class_start = Some(class_start.map_or(current.start_byte(), |seen: usize| {
19225                        seen.min(current.start_byte())
19226                    }));
19227                }
19228                "template" => {
19229                    template_start =
19230                        Some(template_start.map_or(current.start_byte(), |seen: usize| {
19231                            seen.min(current.start_byte())
19232                        }));
19233                }
19234                _ => {}
19235            }
19236        }
19237        let mut cursor = current.walk();
19238        stack.extend(current.children(&mut cursor));
19239    }
19240    if preserved_callable.is_some_and(|callable| {
19241        class_start.is_none_or(|class_start| class_start >= callable.start_byte())
19242    }) {
19243        return None;
19244    }
19245    if let Some(class_start) = class_start {
19246        start = template_start
19247            .filter(|template_start| *template_start < class_start)
19248            .unwrap_or(class_start);
19249    }
19250    Some((start, class_start))
19251}
19252
19253/// Locate a sentinel-prefixed class whose malformed declaration was split across
19254/// root-level siblings. The true class close is represented structurally as a
19255/// lone `}` error followed by the class's displaced `;`; nested method/body
19256/// errors are not direct siblings of the sentinel node and therefore cannot
19257/// satisfy this pair.
19258fn cpp_sentinel_macro_class_region<'tree>(
19259    node: Node<'tree>,
19260    source: &str,
19261) -> Option<(usize, usize, usize, usize, usize, usize)> {
19262    let (reparse_start, Some(class_start)) = cpp_sentinel_macro_parts(node, source)? else {
19263        return None;
19264    };
19265    let body_open_start = cpp_sentinel_macro_class_body_open(node, class_start)
19266        .or_else(|| cpp_body_node(node).map(|body| body.start_byte()))
19267        .or_else(|| cpp_sentinel_macro_displaced_class_body(node).map(|body| body.start_byte()))?;
19268    if class_start >= body_open_start {
19269        return None;
19270    }
19271    let sibling_close = {
19272        let mut sibling = node.next_named_sibling();
19273        let mut found = None;
19274        while let Some(current) = sibling {
19275            let next = current.next_named_sibling();
19276            if let Some(semicolon) = cpp_stray_close_followed_by_semicolon(current, next, source) {
19277                found = Some((
19278                    current.start_byte(),
19279                    semicolon.end_byte(),
19280                    semicolon.end_position().row + 1,
19281                ));
19282                break;
19283            }
19284            sibling = next;
19285        }
19286        found
19287    };
19288    // A stray `};` sibling is this class's close only when the bounded reparse
19289    // agrees the first body-bearing class ENDS there. When the malformed
19290    // envelope swallowed the class's true close, the scan can promote a much
19291    // later scope's close instead -- in protobuf-generated headers
19292    // (wazuh__wazuh's *.pb.h) the `PROTOBUF_NAMESPACE_CLOSE` sentinel before
19293    // `struct TableStruct_*` paired with the first message class's `};`, making
19294    // the recovered "class body" span whole `namespace {}` blocks and minting
19295    // namespace-scope classes as nested members of the recovered class, which
19296    // tripped the package/short boundary assert in CodeUnit::with_signature_and_fq
19297    // (#2275). On disagreement, fall through to the suffix-reparse boundary
19298    // below, which derives the close from the class node's own balanced body
19299    // range.
19300    let sibling_close = sibling_close.filter(|&(close_start, close_end, _)| {
19301        let Some(tree) = cpp_reparse_region_items(source, reparse_start, close_end) else {
19302            return false;
19303        };
19304        let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
19305        // A region reparse is its own tree and needs its own parent index.
19306        let reparsed_ancestry = ParentIndex::new(tree.root_node());
19307        let Some(reparsed_class) = cpp_sentinel_reparsed_class(
19308            tree.root_node(),
19309            template_node,
19310            source,
19311            &reparsed_ancestry,
19312        ) else {
19313            return false;
19314        };
19315        let body = reparsed_class.body;
19316        body.start_byte() == body_open_start && body.end_byte() == close_start + 1
19317    });
19318    let (class_close_start, class_close_end, class_close_line) =
19319        if let Some((class_close_start, class_close_end, class_close_line)) = sibling_close {
19320            (class_close_start, class_close_end, class_close_line)
19321        } else {
19322            // When the malformed envelope itself is an ERROR, tree-sitter can
19323            // leave the class's balanced close in the source while promoting
19324            // all following members to siblings. Reparse the complete suffix
19325            // and use the first body-bearing class node's own field range as
19326            // the partition boundary. This keeps balancing in tree-sitter and
19327            // preserves the source's original byte offsets.
19328            let tree = cpp_reparse_region_items(source, reparse_start, source.len())?;
19329            let template_node = cpp_sentinel_reparsed_leading_template(tree.root_node());
19330            // A region reparse is its own tree and needs its own parent index.
19331            let reparsed_ancestry = ParentIndex::new(tree.root_node());
19332            let reparsed_class = cpp_sentinel_reparsed_class(
19333                tree.root_node(),
19334                template_node,
19335                source,
19336                &reparsed_ancestry,
19337            )?;
19338            let body = reparsed_class.body;
19339            let class_close_end = body.end_byte();
19340            let class_close_start = class_close_end.checked_sub(1)?;
19341            let class_close_line = body.end_position().row + 1;
19342            (class_close_start, class_close_end, class_close_line)
19343        };
19344    if class_close_start <= class_start {
19345        return None;
19346    }
19347
19348    // Reparse only far enough to expose the class body opening. This is a
19349    // structured check that the candidate really begins with a body-bearing
19350    // class-like item; the original malformed tree cannot provide that node.
19351    let tree = cpp_reparse_region_items(source, reparse_start, class_close_end)?;
19352    let class_root = tree.root_node();
19353    let template_node = cpp_sentinel_reparsed_leading_template(class_root);
19354    // A region reparse is its own tree and needs its own parent index.
19355    let reparsed_ancestry = ParentIndex::new(class_root);
19356    let reparsed_class =
19357        cpp_sentinel_reparsed_class(class_root, template_node, source, &reparsed_ancestry)?;
19358    let body = reparsed_class.body;
19359    // The class body opening must agree with the malformed wrapper's structured
19360    // body field. This rejects an inner nested class while permitting later
19361    // members to remain fragmented as root-level siblings in the bounded parse.
19362    if body.start_byte() != body_open_start {
19363        return None;
19364    }
19365    let body_start = body.start_byte().checked_add(1)?;
19366    (body_start < class_close_start).then_some((
19367        reparse_start,
19368        class_start,
19369        body_start,
19370        class_close_start,
19371        class_close_end,
19372        class_close_line,
19373    ))
19374}
19375
19376/// Find the `{` token immediately following the class/struct/union/enum token
19377/// at `class_start` in the malformed tree. The token is anonymous in the C++
19378/// grammar, so this deliberately walks all children (not only named children)
19379/// and relies on sibling structure rather than source-text searching.
19380fn cpp_sentinel_macro_class_body_open(node: Node<'_>, class_start: usize) -> Option<usize> {
19381    let mut stack = vec![node];
19382    while let Some(current) = stack.pop() {
19383        if current.start_byte() == class_start
19384            && matches!(current.kind(), "class" | "struct" | "union" | "enum")
19385        {
19386            // The macro-declaration grammar keeps the class whole: the token's
19387            // specifier ancestor owns the body, and its `{` is the body's
19388            // first child rather than a later sibling of the token.
19389            let mut ancestor = current.parent();
19390            while let Some(parent) = ancestor {
19391                if matches!(
19392                    parent.kind(),
19393                    "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19394                ) {
19395                    if let Some(body) = cpp_body_node(parent) {
19396                        return body
19397                            .child(0)
19398                            .filter(|open| open.kind() == "{")
19399                            .map(|open| open.start_byte());
19400                    }
19401                    break;
19402                }
19403                if matches!(parent.kind(), "ERROR" | "declaration") {
19404                    break;
19405                }
19406                ancestor = parent.parent();
19407            }
19408            let mut sibling = current.next_sibling();
19409            while let Some(candidate) = sibling {
19410                if candidate.kind() == "{" {
19411                    return Some(candidate.start_byte());
19412                }
19413                sibling = candidate.next_sibling();
19414            }
19415        }
19416        let mut cursor = current.walk();
19417        stack.extend(current.children(&mut cursor));
19418    }
19419    None
19420}
19421
19422/// The class body that tree-sitter displaced out of a sentinel-prefixed
19423/// declaration and left as the malformed node's next sibling.
19424///
19425/// When the sentinel envelope reduces to a bare `ERROR` -- `ABSL_NAMESPACE_BEGIN
19426/// template <typename T> class ABSL_ATTRIBUTE_VIEW Span` -- the class token is
19427/// the last child of that `ERROR` and its `{` opens a sibling
19428/// `compound_statement` instead. The body is still the malformed tree's own
19429/// structured token, which is what the caller's `body.start_byte() !=
19430/// body_open_start` agreement check needs; it just is not reachable by walking
19431/// forward from the class token inside the node.
19432fn cpp_sentinel_macro_displaced_class_body(node: Node<'_>) -> Option<Node<'_>> {
19433    node.next_named_sibling()
19434        .filter(|sibling| sibling.kind() == "compound_statement")
19435}
19436
19437fn cpp_sentinel_macro_region(node: Node<'_>, source: &str) -> Option<(usize, usize)> {
19438    let (start, class_start) = cpp_sentinel_macro_parts(node, source)?;
19439    let mut end = if class_start.is_some() {
19440        cpp_macro_prefixed_class_end(source, start)?
19441    } else {
19442        node.end_byte()
19443    };
19444    if class_start.is_none()
19445        && let Some(namespace_end) = cpp_sentinel_following_namespace_end(node, source)
19446    {
19447        end = end.max(namespace_end);
19448    }
19449    let mut sibling = node.next_named_sibling();
19450    while let Some(current) = sibling {
19451        if !cpp_is_stray_semicolon(current, source) {
19452            break;
19453        }
19454        end = current.end_byte();
19455        sibling = current.next_named_sibling();
19456    }
19457    (start < end).then_some((start, end))
19458}
19459
19460/// Extend a sentinel reparse through a following namespace that tree-sitter
19461/// flattened into the sentinel node's sibling list.
19462///
19463/// Fmt places `FMT_END_EXPORT` immediately before `namespace detail`. The
19464/// unknown macro becomes a false function return type and consumes the first
19465/// namespace body. A second `namespace detail` then loses its enclosing node:
19466/// tree-sitter retains the `namespace`, name, and `{` as direct siblings, but
19467/// attaches its declarations to the surrounding error tree. Reparse from that
19468/// structured keyword so tree-sitter, rather than a source-text brace scan,
19469/// supplies the complete namespace boundary.
19470fn cpp_sentinel_following_namespace_end(node: Node<'_>, source: &str) -> Option<usize> {
19471    let mut sibling = node.next_sibling();
19472    let keyword = loop {
19473        let candidate = sibling?;
19474        sibling = candidate.next_sibling();
19475        if candidate.kind() != "comment" {
19476            break candidate;
19477        }
19478    };
19479    if keyword.kind() != "namespace" {
19480        return None;
19481    }
19482    let name = loop {
19483        let candidate = sibling?;
19484        sibling = candidate.next_sibling();
19485        if candidate.kind() != "comment" {
19486            break candidate;
19487        }
19488    };
19489    if cpp_namespace_name_components(name, source).is_empty() {
19490        return None;
19491    }
19492    let open = loop {
19493        let candidate = sibling?;
19494        sibling = candidate.next_sibling();
19495        if candidate.kind() != "comment" {
19496            break candidate;
19497        }
19498    };
19499    if open.kind() != "{" {
19500        return None;
19501    }
19502
19503    let tree = cpp_reparse_region_items(source, keyword.start_byte(), source.len())?;
19504    let root = tree.root_node();
19505    let mut cursor = root.walk();
19506    let namespace = root
19507        .named_children(&mut cursor)
19508        .find(|candidate| candidate.kind() != "comment")?;
19509    (namespace.kind() == "namespace_definition"
19510        && namespace.start_byte() == keyword.start_byte()
19511        && namespace.child_by_field_name("body").is_some())
19512    .then_some(namespace.end_byte())
19513}
19514
19515/// Parse the source suffix beginning at a structurally recovered class/template
19516/// keyword and return the end of its first body-bearing class item.  The parser,
19517/// rather than a brace scanner, owns nested-body balancing.  This is needed when
19518/// the original error tree truncates the class and scatters later members as
19519/// top-level siblings.
19520fn cpp_macro_prefixed_class_end(source: &str, start: usize) -> Option<usize> {
19521    let tree = cpp_reparse_region_items(source, start, source.len())?;
19522    let root = tree.root_node();
19523    let mut cursor = root.walk();
19524    for item in root.named_children(&mut cursor) {
19525        if item.end_byte() <= start || item.kind() == "comment" {
19526            continue;
19527        }
19528        let mut stack = vec![item];
19529        while let Some(current) = stack.pop() {
19530            if matches!(
19531                current.kind(),
19532                "class_specifier" | "struct_specifier" | "union_specifier" | "enum_specifier"
19533            ) && cpp_body_node(current).is_some()
19534            {
19535                return Some(current.end_byte());
19536            }
19537            let mut cursor = current.walk();
19538            stack.extend(current.named_children(&mut cursor));
19539        }
19540        // The recovered prefix is required to begin with the class item.  If
19541        // the first real item is something else, fail closed rather than skip
19542        // arbitrary source looking for a later class.
19543        return None;
19544    }
19545    None
19546}
19547
19548/// An empty `;` statement: the displaced closing semicolon of a struct/class that
19549/// the sentinel mis-parse split off past the bogus function node.
19550fn cpp_is_stray_semicolon(node: Node<'_>, source: &str) -> bool {
19551    node.kind() == "expression_statement"
19552        && node.named_child_count() == 0
19553        && node_text(node, source).trim() == ";"
19554}
19555
19556fn cpp_stray_close_followed_by_semicolon<'tree>(
19557    close: Node<'tree>,
19558    next: Option<Node<'tree>>,
19559    source: &str,
19560) -> Option<Node<'tree>> {
19561    cpp_is_stray_close_brace(close, source)
19562        .then(|| next.filter(|semicolon| cpp_is_stray_semicolon(*semicolon, source)))
19563        .flatten()
19564}
19565
19566/// Recover the member that follows CPython's object header macro when
19567/// tree-sitter folds both declarations into one malformed field.
19568///
19569/// For `PyObject_HEAD Imaging image;`, the CST has `PyObject_HEAD` as the
19570/// field type, `Imaging` as the field declarator, and a trailing ERROR whose
19571/// sole child is the real field declarator. The tail may wrap that identifier
19572/// in pointer declarators, as in `PyObject_HEAD ImagingObject *image;`; other
19573/// malformed wrappers do not provide the same evidence that this is the
19574/// CPython macro boundary.
19575#[derive(Clone, Copy)]
19576pub(crate) struct RecoveredPyObjectHeadField<'tree> {
19577    pub(crate) type_node: Node<'tree>,
19578    pub(crate) name: Node<'tree>,
19579    pub(crate) declarator: Node<'tree>,
19580}
19581
19582impl RecoveredPyObjectHeadField<'_> {
19583    pub(crate) fn pointer_depth(self) -> i32 {
19584        let mut depth = 0;
19585        let mut current = self.declarator;
19586        while current != self.name {
19587            debug_assert_eq!(current.kind(), "pointer_declarator");
19588            depth += 1;
19589            current = current
19590                .child_by_field_name("declarator")
19591                .expect("recovered PyObject field pointer has an inner declarator");
19592        }
19593        depth
19594    }
19595}
19596
19597pub(crate) fn recovered_pyobject_head_field<'tree>(
19598    node: Node<'tree>,
19599    source: &str,
19600) -> Option<RecoveredPyObjectHeadField<'tree>> {
19601    if node.kind() != "field_declaration" {
19602        return None;
19603    }
19604    let type_node = node.child_by_field_name("type")?;
19605    if type_node.kind() != "type_identifier"
19606        || node_text(type_node, source).trim() != "PyObject_HEAD"
19607    {
19608        return None;
19609    }
19610    let pseudo_declarator = node.child_by_field_name("declarator")?;
19611    let mut cursor = node.walk();
19612    let errors = node
19613        .named_children(&mut cursor)
19614        .filter(|child| child.kind() == "ERROR")
19615        .collect::<Vec<_>>();
19616    let [error] = errors.as_slice() else {
19617        return None;
19618    };
19619    if error.named_child_count() != 1 {
19620        return None;
19621    }
19622    let error_child = error.named_child(0)?;
19623    if pseudo_declarator.kind() == "field_identifier"
19624        && error.start_byte() >= pseudo_declarator.end_byte()
19625        && error_child.kind() == "identifier"
19626    {
19627        return Some(RecoveredPyObjectHeadField {
19628            type_node: pseudo_declarator,
19629            name: error_child,
19630            declarator: error_child,
19631        });
19632    }
19633    if pseudo_declarator.kind() != "pointer_declarator"
19634        || error.end_byte() > pseudo_declarator.start_byte()
19635        || error_child.kind() != "identifier"
19636    {
19637        return None;
19638    }
19639    let mut name = pseudo_declarator;
19640    while name.kind() == "pointer_declarator" {
19641        name = name.child_by_field_name("declarator")?;
19642    }
19643    (name.kind() == "field_identifier").then_some(RecoveredPyObjectHeadField {
19644        type_node: error_child,
19645        name,
19646        declarator: pseudo_declarator,
19647    })
19648}
19649
19650/// Recover the real field name when a leading object-like annotation macro
19651/// displaces a qualified type into tree-sitter's bit-field recovery shape.
19652///
19653/// `static API constexpr std::size_t npos = ...;` is parsed as `API` in the
19654/// type field, `std` as the field declarator, and `::size_t npos = ...` as a
19655/// `bitfield_clause` containing an error plus an assignment.  The assignment's
19656/// left field is the only structured declaration name in that malformed tail.
19657/// A real bit-field is excluded by the all-caps macro type and required error.
19658fn recovered_macro_qualified_field_clause<'tree>(
19659    node: Node<'tree>,
19660    source: &str,
19661) -> Option<Node<'tree>> {
19662    if node.kind() != "field_declaration" {
19663        return None;
19664    }
19665    let macro_type = node.child_by_field_name("type")?;
19666    if macro_type.kind() != "type_identifier"
19667        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19668    {
19669        return None;
19670    }
19671    let mut cursor = node.walk();
19672    node.named_children(&mut cursor)
19673        .find(|child| child.kind() == "bitfield_clause")
19674}
19675
19676fn recovered_macro_qualified_field_declarators<'tree>(
19677    node: Node<'tree>,
19678    source: &str,
19679) -> Option<Vec<Node<'tree>>> {
19680    let pseudo_declarator = node.child_by_field_name("declarator")?;
19681    if pseudo_declarator.kind() != "field_identifier" {
19682        return None;
19683    }
19684    let clause = recovered_macro_qualified_field_clause(node, source)?;
19685    if !(0..clause.named_child_count()).any(|index| {
19686        clause
19687            .named_child(index)
19688            .is_some_and(|child| child.kind() == "ERROR")
19689    }) {
19690        return None;
19691    }
19692    let mut recovered = Vec::new();
19693    let mut stack = vec![clause];
19694    while let Some(current) = stack.pop() {
19695        if current.kind() == "assignment_expression"
19696            && let Some(left) = current.child_by_field_name("left")
19697            && extract_variable_name(left, source).is_some()
19698        {
19699            recovered.push(left);
19700            break;
19701        }
19702        let mut cursor = current.walk();
19703        stack.extend(current.named_children(&mut cursor));
19704    }
19705    if recovered.is_empty() {
19706        return None;
19707    }
19708    let mut cursor = node.walk();
19709    recovered.extend(
19710        node.children_by_field_name("declarator", &mut cursor)
19711            .filter(|declarator| !same_node(*declarator, pseudo_declarator)),
19712    );
19713    Some(recovered)
19714}
19715
19716/// Recover a macro-qualified constructor that tree-sitter represents as one
19717/// field declaration. The constructor call remains inside the direct recovery
19718/// error, while each member initializer becomes a false function declarator.
19719/// The class owner proves the constructor name and lets the caller ignore those
19720/// initializer declarators.
19721fn recovered_macro_qualified_constructor_call<'tree>(
19722    node: Node<'tree>,
19723    class_name: &str,
19724    source: &str,
19725) -> Option<Node<'tree>> {
19726    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19727    let error = bitfield
19728        .named_child(0)
19729        .filter(|child| child.kind() == "ERROR")?;
19730    let mut stack = vec![error];
19731    while let Some(current) = stack.pop() {
19732        if current.kind() == "call_expression"
19733            && current
19734                .child_by_field_name("function")
19735                .is_some_and(|function| node_text(function, source) == class_name)
19736            && current
19737                .child_by_field_name("arguments")
19738                .is_some_and(|arguments| arguments.kind() == "argument_list")
19739        {
19740            return Some(current);
19741        }
19742        let mut cursor = current.walk();
19743        stack.extend(current.named_children(&mut cursor));
19744    }
19745    None
19746}
19747
19748/// The constructor head of a macro-decorated constructor that the
19749/// macro-declaration grammar reads as one field declaration
19750/// (`MSC_WARNING(suppress : 26495) CONSTEXPR Buffer(grow_fun grow) noexcept
19751/// : size_(size), grow_(grow) {}`, #1838). The macro is the field's `type`,
19752/// the constructor head is its first declarator (possibly inside a
19753/// `macro_modifier_declarator`), the first member initializer is a
19754/// `bitfield_clause` call, every later initializer is another function
19755/// declarator, and the body is the `default_value` initializer list. A
19756/// constructor declares no type, and no real field pairs a callable declarator
19757/// with a bitfield, so a head naming the enclosing class beside a bitfield
19758/// call is this shape. The caller publishes the head and none of the
19759/// initializer declarators.
19760fn recovered_macro_initialized_constructor_declarator<'tree>(
19761    node: Node<'tree>,
19762    class_name: &str,
19763    source: &str,
19764) -> Option<Node<'tree>> {
19765    if node.kind() != "field_declaration" {
19766        return None;
19767    }
19768    let constructor = extract_function_declarator(node.child_by_field_name("declarator")?)?;
19769    if cpp_function_declarator_name_node(constructor)
19770        .is_none_or(|name| node_text(name, source) != class_name)
19771    {
19772        return None;
19773    }
19774    let mut cursor = node.walk();
19775    let initializer = node
19776        .named_children(&mut cursor)
19777        .find(|child| child.kind() == "bitfield_clause")?
19778        .named_child(0)?;
19779    (initializer.kind() == "call_expression").then_some(constructor)
19780}
19781
19782/// Recover a macro-qualified member function declaration that tree-sitter
19783/// represents as a pseudo-field. An object-like export macro before a qualified
19784/// return type can displace the namespace and type into an ERROR/bitfield
19785/// recovery, leaving the callable as a structured `call_expression`.
19786///
19787/// The caller must route this shape before ordinary declarator classification;
19788/// otherwise the displaced namespace identifier is published as a field.
19789fn recovered_macro_qualified_function_call<'tree>(
19790    node: Node<'tree>,
19791    source: &str,
19792) -> Option<Node<'tree>> {
19793    let bitfield = recovered_macro_qualified_field_clause(node, source)?;
19794    let declarator = node.child_by_field_name("declarator")?;
19795    if declarator.kind() != "field_identifier" {
19796        return None;
19797    }
19798    let mut cursor = node.walk();
19799    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
19800    if !named.iter().any(|child| {
19801        child.kind() == "storage_class_specifier"
19802            && normalize_cpp_whitespace(node_text(*child, source)) == "static"
19803    }) {
19804        return None;
19805    }
19806    let mut bitfield_cursor = bitfield.walk();
19807    let payload = bitfield
19808        .named_children(&mut bitfield_cursor)
19809        .collect::<Vec<_>>();
19810    let [displaced_error, call] = payload.as_slice() else {
19811        return None;
19812    };
19813    if displaced_error.kind() != "ERROR"
19814        || displaced_error.named_child_count() != 1
19815        || displaced_error
19816            .named_child(0)
19817            .is_none_or(|child| child.kind() != "identifier")
19818        || call.kind() != "call_expression"
19819        || call
19820            .child_by_field_name("function")
19821            .is_none_or(|function| !matches!(function.kind(), "identifier" | "field_identifier"))
19822        || call
19823            .child_by_field_name("arguments")
19824            .is_none_or(|arguments| arguments.kind() != "argument_list")
19825    {
19826        return None;
19827    }
19828    Some(*call)
19829}
19830
19831/// The real constructor declarator of a macro-decorated constructor the
19832/// macro-declaration grammar reads as an explicit specifier, an `ERROR`
19833/// holding the macro and the constructor head as a `macro_modifier_declarator`,
19834/// and the member initializer mis-read as the function's own declarator
19835/// (`explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}`, #2552 shape 1).
19836/// The constructor head inside the `ERROR` is what names the class; the
19837/// declarator the `function_definition` fields is the initializer, never the
19838/// member.
19839fn recovered_macro_decorated_constructor_declarator<'tree>(
19840    node: Node<'tree>,
19841    class_name: &str,
19842    source: &str,
19843) -> Option<Node<'tree>> {
19844    if node.kind() != "function_definition" {
19845        return None;
19846    }
19847    let mut cursor = node.walk();
19848    let mut heads = Vec::new();
19849    for error in node
19850        .named_children(&mut cursor)
19851        .filter(|child| child.kind() == "ERROR")
19852    {
19853        let mut stack = vec![error];
19854        while let Some(current) = stack.pop() {
19855            let is_constructor_head = match current.kind() {
19856                "macro_modifier_declarator" | "function_declarator" => {
19857                    extract_function_declarator(current).is_some_and(|declarator| {
19858                        cpp_function_declarator_name_node(declarator)
19859                            .is_some_and(|name| node_text(name, source) == class_name)
19860                    })
19861                }
19862                _ => false,
19863            };
19864            if is_constructor_head && let Some(declarator) = extract_function_declarator(current) {
19865                heads.push(declarator);
19866                continue;
19867            }
19868            let mut cursor = current.walk();
19869            stack.extend(current.named_children(&mut cursor));
19870        }
19871    }
19872    let [head] = heads.as_slice() else {
19873        return None;
19874    };
19875    Some(*head)
19876}
19877
19878fn recovered_macro_qualified_function_parameters(
19879    arguments: Node<'_>,
19880    source: &str,
19881) -> Option<(String, Vec<String>, [u8; 32])> {
19882    use brokk_bifrost_core::analyzer::tree_walk::{
19883        overload_discriminator, overload_parameter_tokens,
19884    };
19885    if arguments.kind() != "argument_list" {
19886        return None;
19887    }
19888    let mut cursor = arguments.walk();
19889    let named = arguments.named_children(&mut cursor).collect::<Vec<_>>();
19890    if named.is_empty() {
19891        return Some((
19892            "()".to_string(),
19893            Vec::new(),
19894            overload_discriminator(Vec::<String>::new()),
19895        ));
19896    }
19897    let mut types = Vec::new();
19898    let mut tokens = Vec::new();
19899    let mut labels = Vec::new();
19900    let mut index = 0;
19901    while index < named.len() {
19902        let parameter_type = named[index];
19903        let parameter_name = named.get(index + 1).copied()?;
19904        if !matches!(
19905            parameter_type.kind(),
19906            "identifier" | "type_identifier" | "qualified_identifier" | "template_type"
19907        ) || parameter_name.kind() != "ERROR"
19908            || parameter_name.named_child_count() != 1
19909            || parameter_name
19910                .named_child(0)
19911                .is_none_or(|child| !matches!(child.kind(), "identifier" | "field_identifier"))
19912        {
19913            return None;
19914        }
19915        let parameter_name = parameter_name.named_child(0)?;
19916        tokens.extend(overload_parameter_tokens(Some(parameter_type), source));
19917        types.push(normalize_cpp_whitespace(node_text(parameter_type, source)));
19918        labels.push(normalize_cpp_whitespace(node_text(parameter_name, source)));
19919        index += 2;
19920    }
19921    Some((
19922        format!("({})", types.join(", ")),
19923        labels,
19924        overload_discriminator(tokens),
19925    ))
19926}
19927
19928/// Recognize the phantom field tree-sitter emits for a macro-qualified
19929/// function return type.  For example,
19930/// `static API result_type ThresholdForSmallA() { ... }` can become a
19931/// `field_declaration` (`API` as the type and `result_type` as a field name)
19932/// followed by a clean `function_definition` for `ThresholdForSmallA`.
19933///
19934/// Keep this predicate entirely tied to the CST envelope: the type must be an
19935/// all-caps macro token, the pseudo-declarator must be a bare field identifier,
19936/// the declaration must carry a missing semicolon rather than a real one, and
19937/// the immediate named sibling must expose a function declarator.  A real
19938/// macro-decorated field with an explicit semicolon therefore remains a field.
19939pub fn recovered_macro_return_type_node<'tree>(
19940    node: Node<'tree>,
19941    source: &str,
19942) -> Option<Node<'tree>> {
19943    if node.kind() != "field_declaration" {
19944        return None;
19945    }
19946    let macro_type = node.child_by_field_name("type")?;
19947    if macro_type.kind() != "type_identifier"
19948        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_type, source)))
19949    {
19950        return None;
19951    }
19952    let declarator = node.child_by_field_name("declarator")?;
19953    if declarator.kind() != "field_identifier" || node_text(declarator, source).trim().is_empty() {
19954        return None;
19955    }
19956    let mut has_missing_semicolon = false;
19957    let mut has_real_semicolon = false;
19958    for child in children_iter(node) {
19959        if child.kind() != ";" {
19960            continue;
19961        }
19962        if child.is_missing() {
19963            has_missing_semicolon = true;
19964        } else {
19965            has_real_semicolon = true;
19966        }
19967    }
19968    if !has_missing_semicolon || has_real_semicolon {
19969        return None;
19970    }
19971    let mut next = node.next_named_sibling();
19972    while next.is_some_and(|sibling| sibling.kind() == "comment") {
19973        next = next.and_then(|sibling| sibling.next_named_sibling());
19974    }
19975    let next = next?;
19976    if next.kind() != "function_definition" || next.child_by_field_name("type").is_some() {
19977        return None;
19978    }
19979    let function_declarator = next.child_by_field_name("declarator")?;
19980    extract_function_declarator(function_declarator).map(|_| declarator)
19981}
19982
19983/// The omitted-semicolon class field the macro-declaration grammar folds into
19984/// the head of the normally typed function that follows it. The older grammar
19985/// left `DWORD preserved_field` ahead of `void PreserveFollowingFunction() {}`
19986/// as a damaged `field_declaration`; the macro-declaration grammar reads the
19987/// same pair as two leading `macro_modifier`s of the clean function
19988/// definition. Only an exact modifier pair ahead of a real type field and a
19989/// function declarator is a field: a lone modifier ahead of the type is the
19990/// macro-return shape (`static API result_type f() { ... }`), which carries
19991/// no field.
19992fn omitted_semicolon_macro_field_pair(node: Node<'_>) -> Option<(Node<'_>, Node<'_>)> {
19993    if node.kind() != "function_definition" {
19994        return None;
19995    }
19996    let type_node = node.child_by_field_name("type")?;
19997    let declarator = node.child_by_field_name("declarator")?;
19998    extract_function_declarator(declarator)?;
19999    let mut cursor = node.walk();
20000    let head = node
20001        .named_children(&mut cursor)
20002        .take_while(|child| *child != type_node)
20003        .filter(|child| child.kind() != "comment")
20004        .collect::<Vec<_>>();
20005    let [type_modifier, name_modifier] = head.as_slice() else {
20006        return None;
20007    };
20008    macro_modifier_identifier(*type_modifier)?;
20009    macro_modifier_identifier(*name_modifier)?;
20010    Some((*type_modifier, *name_modifier))
20011}
20012
20013/// The stand-in field evidence in a clean macro-return function definition:
20014/// `CUTLASS_DEVICE static OperandLayout Layout() { ... }` has a
20015/// `macro_modifier` head and a return type naming an active template type
20016/// parameter. The older grammar read the same head as a phantom
20017/// `field_declaration` named after the type, and dependent calls like
20018/// `OperandLayout::packed` resolved to that field (#1129). Returns the head's
20019/// first token and the type node; the active-parameter gate is the same one
20020/// the phantom-field suppression used, so a macro return type naming no
20021/// template parameter (`result_type`) recovers nothing.
20022fn macro_return_template_parameter_field<'tree>(
20023    node: Node<'tree>,
20024    source: &str,
20025    ancestry: &ParentIndex<'tree>,
20026) -> Option<(Node<'tree>, Node<'tree>)> {
20027    if node.kind() != "function_definition" {
20028        return None;
20029    }
20030    let type_node = node.child_by_field_name("type")?;
20031    if type_node.kind() != "type_identifier" {
20032        return None;
20033    }
20034    let declarator = node.child_by_field_name("declarator")?;
20035    extract_function_declarator(declarator)?;
20036    let mut cursor = node.walk();
20037    let head = node
20038        .named_children(&mut cursor)
20039        .take_while(|child| *child != type_node)
20040        .filter(|child| child.kind() != "comment")
20041        .collect::<Vec<_>>();
20042    let [first, ..] = head.as_slice() else {
20043        return None;
20044    };
20045    if !head
20046        .iter()
20047        .all(|child| matches!(child.kind(), "macro_modifier" | "storage_class_specifier"))
20048        || !head.iter().any(|child| child.kind() == "macro_modifier")
20049    {
20050        return None;
20051    }
20052    let name = normalize_cpp_whitespace(node_text(type_node, source));
20053    if !cpp_active_template_type_parameter(node, &name, source, ancestry) {
20054        return None;
20055    }
20056    Some((*first, type_node))
20057}
20058
20059/// Whether `name` is a type parameter of a template declaration that lexically
20060/// encloses `node`. The malformed macro-return field uses the parameter name as
20061/// its pseudo-declarator; preserving that field is necessary to publish a
20062/// definition for dependent calls such as `OperandLayout::packed`. Walk the AST
20063/// ancestors instead of interpreting source text so nested templates and
20064/// parser-recovered regions retain their real lexical scopes.
20065pub(crate) fn cpp_active_template_type_parameter<'tree>(
20066    node: Node<'tree>,
20067    name: &str,
20068    source: &str,
20069    ancestry: &ParentIndex<'tree>,
20070) -> bool {
20071    let mut ancestor = ancestry.parent(node);
20072    while let Some(current) = ancestor {
20073        if current.kind() == "template_declaration"
20074            && let Some(parameters) = current.child_by_field_name("parameters")
20075            && cpp_template_parameter_name_matches(parameters, name, |parameter| {
20076                cpp_template_parameter_name(parameter, source)
20077            })
20078            .type_parameter
20079        {
20080            return true;
20081        }
20082        ancestor = ancestry.parent(current);
20083    }
20084    false
20085}
20086
20087/// Reparse the region `[start, end)` of `source` as C++, confined to the region
20088/// via included ranges so every reparsed node keeps its original byte offset and
20089/// line number. The existing visitors read node text from the original source,
20090/// so ranges and ownership stay byte/line-exact. Mirrors the Rust #1015
20091/// `parse_rust_region_tree` technique.
20092fn cpp_reparse_region_items(source: &str, start: usize, end: usize) -> Option<Tree> {
20093    parse_source_region(&tree_sitter_cpp::LANGUAGE.into(), source, start, end)
20094}
20095
20096fn cpp_error_swallowed_function_declaration_range(node: Node<'_>) -> Option<(usize, usize)> {
20097    if node.kind() != "function_declarator" || node.parent()?.kind() != "ERROR" {
20098        return None;
20099    }
20100    let semicolon = node.next_sibling()?;
20101    if semicolon.kind() != ";" || semicolon.is_missing() {
20102        return None;
20103    }
20104    let row = node.start_position().row;
20105    let mut start = node.start_byte();
20106    let mut sibling = node.prev_sibling();
20107    while let Some(previous) = sibling.filter(|previous| previous.start_position().row == row) {
20108        if previous.kind() == ";" {
20109            break;
20110        }
20111        start = previous.start_byte();
20112        sibling = previous.prev_sibling();
20113    }
20114    (start < node.start_byte()).then_some((start, semicolon.end_byte()))
20115}
20116
20117/// One `RET name _(( args ));`-style K&R prototype-macro invocation
20118/// recognized by [`cpp_prototype_macro_candidates`] (issue #2932): the three
20119/// byte spans a clean reparse needs, in original-source order.
20120struct PrototypeMacroCandidate {
20121    /// Where the return type and declared name begin.
20122    run_start: usize,
20123    /// Byte where the macro token identifier (`_`, `__P`, ...) begins: the
20124    /// exclusive end of the first reparse span.
20125    identifier_start: usize,
20126    /// The inner `(`'s start byte: the start of the second reparse span.
20127    /// This paren is kept so the reparse sees exactly one parameter-list
20128    /// paren pair.
20129    inner_open_start: usize,
20130    /// Byte just past the inner `)`: the end of the second reparse span.
20131    inner_close_end: usize,
20132    /// Byte just past the outer `)`: the start of the third reparse span.
20133    outer_close_end: usize,
20134    /// Byte just past the terminating `;`: the end of the third reparse
20135    /// span, and of the whole candidate.
20136    semicolon_end: usize,
20137}
20138
20139impl PrototypeMacroCandidate {
20140    /// The three spans [`parse_source_ranges_with_cancellation`] parses as
20141    /// one included-range tree: the return type and name, the parenthesized
20142    /// argument list, and the terminating `;`. The macro token and its outer
20143    /// wrapping parenthesis are omitted -- deleting them is exactly what the
20144    /// (absent) preprocessor macro expansion would do.
20145    fn ranges(&self) -> [(usize, usize); 3] {
20146        [
20147            (self.run_start, self.identifier_start),
20148            (self.inner_open_start, self.inner_close_end),
20149            (self.outer_close_end, self.semicolon_end),
20150        ]
20151    }
20152}
20153
20154/// The class a class-body prototype-macro reparse keeps around the member
20155/// (#6902): the class head through its `{`, the access label the member is
20156/// written under, and the class's `}`, at their original offsets. In that
20157/// parse the recovered prototype is a real member of a real class body, so its
20158/// `static` and its place on the body's access ladder state its modifiers
20159/// exactly as an ordinary member's do.
20160struct PrototypeMacroClassEnvelope<'tree> {
20161    /// The class's enclosing node, which is the recovery's origin.
20162    class_parent: Node<'tree>,
20163    /// The class head through the body's `{`.
20164    head: (usize, usize),
20165    /// The body's `{`, which finds the reparsed body.
20166    open: (usize, usize),
20167    /// The last `access_specifier` the body writes before the member, through
20168    /// its `:`.
20169    label: Option<(usize, usize)>,
20170    /// The body's `}`.
20171    close: (usize, usize),
20172}
20173
20174/// The [`PrototypeMacroClassEnvelope`] of one class-body member, read forward
20175/// with one cursor as [`cpp_member_declared_visibility`] reads the access
20176/// ladder. A member nested in anything but preprocessor conditionals, or under
20177/// a label a macro invocation swallowed, has no envelope that reproduces its
20178/// position, so it stays unrecovered rather than taking another access.
20179fn cpp_prototype_macro_class_envelope<'tree>(
20180    member: Node<'tree>,
20181    source: &str,
20182    ancestry: &ParentIndex<'tree>,
20183) -> Option<PrototypeMacroClassEnvelope<'tree>> {
20184    let mut list_level = member;
20185    let body = loop {
20186        let parent = ancestry.parent(list_level)?;
20187        if parent.kind() == "field_declaration_list" {
20188            break parent;
20189        }
20190        if !is_cpp_preprocessor_conditional(parent) {
20191            return None;
20192        }
20193        list_level = parent;
20194    };
20195    let class_like = ancestry.parent(body)?;
20196    let class_parent = ancestry.parent(class_like)?;
20197    let open = body.child(0).filter(|open| open.kind() == "{")?;
20198    let close = body
20199        .child(body.child_count().checked_sub(1)?)
20200        .filter(|close| close.kind() == "}" && !close.is_missing())?;
20201    let mut label = None;
20202    let mut under_swallowed_label = false;
20203    let mut cursor = body.walk();
20204    for child in body.children(&mut cursor) {
20205        if child == list_level {
20206            break;
20207        }
20208        if child.kind() == "access_specifier" {
20209            // The grammar leaves the label's `:` to the body.
20210            let colon = child
20211                .next_sibling()
20212                .filter(|colon| colon.kind() == ":" && !colon.is_missing())?;
20213            label = Some((child.start_byte(), colon.end_byte()));
20214            under_swallowed_label = false;
20215        } else if cpp_macro_swallowed_access_label(child, source).is_some() {
20216            under_swallowed_label = true;
20217        }
20218    }
20219    if under_swallowed_label {
20220        return None;
20221    }
20222    Some(PrototypeMacroClassEnvelope {
20223        class_parent,
20224        head: (class_like.start_byte(), open.end_byte()),
20225        open: (open.start_byte(), open.end_byte()),
20226        label,
20227        close: (close.start_byte(), close.end_byte()),
20228    })
20229}
20230
20231/// A live terminating semicolon owned directly by `node`.
20232fn cpp_direct_semicolon(node: Node<'_>) -> Option<Node<'_>> {
20233    node.child(node.child_count().checked_sub(1)?)
20234        .filter(|child| child.kind() == ";" && !child.is_missing())
20235}
20236
20237/// The known pre-ANSI prototype macros whose expansion is exactly their one
20238/// argument. The malformed CST cannot prove that an arbitrary identifier has
20239/// that definition, so unknown spellings fail closed rather than turning an
20240/// ordinary broken declaration into a Function (issue #2932).
20241fn cpp_is_prototype_macro_identifier(node: Node<'_>, source: &str) -> bool {
20242    matches!(
20243        node.kind(),
20244        "identifier" | "type_identifier" | "field_identifier" | "namespace_identifier"
20245    ) && matches!(
20246        normalize_cpp_whitespace(node_text(node, source)).as_str(),
20247        "_" | "__P" | "OF" | "PROTO"
20248    )
20249}
20250
20251/// Read tree-sitter's recovery for `declared_name MACRO`: a
20252/// `qualified_identifier` whose separator is MISSING, whose `scope` is the
20253/// declared name, and whose `name` is a known prototype macro.
20254fn cpp_prototype_macro_qualified_parts<'tree>(
20255    node: Node<'tree>,
20256    source: &str,
20257) -> Option<(Node<'tree>, Node<'tree>)> {
20258    if node.kind() != "qualified_identifier" {
20259        return None;
20260    }
20261    let declared_name = node
20262        .child_by_field_name("scope")
20263        .filter(|scope| matches!(scope.kind(), "namespace_identifier" | "identifier"))?;
20264    let macro_name = macro_decorated_unqualified_name(node)?;
20265    cpp_is_prototype_macro_identifier(macro_name, source).then_some((declared_name, macro_name))
20266}
20267
20268/// The inner parenthesized node of tree-sitter's structured
20269/// `MACRO((parameters))` argument list. Keeping this node's exact range drops
20270/// the macro invocation's outer parentheses while retaining the one pair an
20271/// ordinary function declarator needs. No delimiter search is involved: both
20272/// pairs and their ownership come from the CST.
20273fn cpp_prototype_macro_inner_arguments(arguments: Node<'_>) -> Option<Node<'_>> {
20274    if arguments.kind() != "argument_list"
20275        || arguments.named_child_count() != 1
20276        || arguments.child_count() != 3
20277        || arguments
20278            .child(0)
20279            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20280        || arguments
20281            .child(2)
20282            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20283    {
20284        return None;
20285    }
20286    let inner = arguments.named_child(0)?;
20287    let close_index = match inner.kind() {
20288        "parenthesized_expression" => inner.child_count().checked_sub(1)?,
20289        // tree-sitter completes the C++ cast production with a zero-width
20290        // value after the live close parenthesis.
20291        "cast_expression" => inner.child_count().checked_sub(2)?,
20292        _ => return None,
20293    };
20294    (inner
20295        .child(0)
20296        .is_some_and(|open| open.kind() == "(" && !open.is_missing())
20297        && inner
20298            .child(close_index)
20299            .is_some_and(|close| close.kind() == ")" && !close.is_missing()))
20300    .then_some(inner)
20301}
20302
20303fn cpp_prototype_macro_candidate_from_init_declaration(
20304    declaration: Node<'_>,
20305    source: &str,
20306) -> Option<PrototypeMacroCandidate> {
20307    let init = declaration
20308        .child_by_field_name("declarator")
20309        .filter(|declarator| declarator.kind() == "init_declarator")?;
20310    let malformed_declarator = init.child_by_field_name("declarator")?;
20311    let (declared_name, macro_name) = if malformed_declarator.kind() == "qualified_identifier" {
20312        cpp_prototype_macro_qualified_parts(malformed_declarator, source)?
20313    } else {
20314        if !cpp_is_prototype_macro_identifier(malformed_declarator, source) {
20315            return None;
20316        }
20317        let declared_name_error = init
20318            .prev_named_sibling()
20319            .filter(|previous| previous.kind() == "ERROR" && previous.named_child_count() == 1)?;
20320        let declared_name = declared_name_error
20321            .named_child(0)
20322            .filter(|name| matches!(name.kind(), "identifier" | "field_identifier"))?;
20323        (declared_name, malformed_declarator)
20324    };
20325    let arguments = init
20326        .child_by_field_name("value")
20327        .filter(|value| value.kind() == "argument_list")?;
20328    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20329    let semicolon = cpp_direct_semicolon(declaration)?;
20330    let return_type = declaration.child_by_field_name("type")?;
20331    if return_type.end_byte() > declared_name.start_byte()
20332        || declared_name.end_byte() > macro_name.start_byte()
20333        || macro_name.end_byte() > arguments.start_byte()
20334        || arguments.end_byte() > semicolon.start_byte()
20335    {
20336        return None;
20337    }
20338    Some(cpp_prototype_macro_candidate_from_parts(
20339        declaration.start_byte(),
20340        macro_name,
20341        inner,
20342        arguments,
20343        semicolon,
20344    ))
20345}
20346
20347fn cpp_prototype_macro_candidate_from_qualified_declaration(
20348    declaration: Node<'_>,
20349    source: &str,
20350) -> Option<PrototypeMacroCandidate> {
20351    let qualified = declaration
20352        .child_by_field_name("declarator")
20353        .filter(|declarator| declarator.kind() == "qualified_identifier")?;
20354    let (_, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20355    let open_error = qualified
20356        .next_named_sibling()
20357        .filter(|next| next.kind() == "ERROR")?;
20358    let close_error = last_named_child(declaration)
20359        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20360    if open_error.child_count() < 3
20361        || open_error
20362            .child(0)
20363            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20364        || open_error
20365            .child(1)
20366            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20367        || close_error.child_count() != 2
20368        || close_error
20369            .child(0)
20370            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20371        || close_error
20372            .child(1)
20373            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20374    {
20375        return None;
20376    }
20377    let inner_open = open_error.child(1)?;
20378    let inner_close = close_error.child(0)?;
20379    let outer_close = close_error.child(1)?;
20380    let semicolon = cpp_direct_semicolon(declaration)?;
20381    let return_type = declaration.child_by_field_name("type")?;
20382    if return_type.end_byte() > qualified.start_byte()
20383        || macro_name.end_byte() > open_error.start_byte()
20384        || inner_open.start_byte() > inner_close.end_byte()
20385        || inner_close.end_byte() > outer_close.start_byte()
20386        || outer_close.end_byte() > semicolon.start_byte()
20387    {
20388        return None;
20389    }
20390    Some(PrototypeMacroCandidate {
20391        run_start: declaration.start_byte(),
20392        identifier_start: macro_name.start_byte(),
20393        inner_open_start: inner_open.start_byte(),
20394        inner_close_end: inner_close.end_byte(),
20395        outer_close_end: outer_close.end_byte(),
20396        semicolon_end: semicolon.end_byte(),
20397    })
20398}
20399
20400fn cpp_prototype_macro_candidate_from_pointer_expression(
20401    statement: Node<'_>,
20402    source: &str,
20403) -> Option<PrototypeMacroCandidate> {
20404    if statement.kind() != "expression_statement"
20405        || statement.named_child_count() != 1
20406        || !statement.has_error()
20407    {
20408        return None;
20409    }
20410    let expansion = statement
20411        .named_child(0)
20412        .filter(|child| child.kind() == "parameter_pack_expansion")?;
20413    let binary = expansion
20414        .child_by_field_name("pattern")
20415        .filter(|pattern| pattern.kind() == "binary_expression")?;
20416    if binary.child_count() != 3
20417        || binary
20418            .child(1)
20419            .is_none_or(|operator| operator.kind() != "*" || operator.is_missing())
20420        || expansion
20421            .child(expansion.child_count().checked_sub(1)?)
20422            .is_none_or(|ellipsis| ellipsis.kind() != "..." || !ellipsis.is_missing())
20423    {
20424        return None;
20425    }
20426    let return_type = binary.child_by_field_name("left")?;
20427    let call = binary
20428        .child_by_field_name("right")
20429        .filter(|right| right.kind() == "call_expression")?;
20430    let qualified = call.child_by_field_name("function")?;
20431    let (declared_name, macro_name) = cpp_prototype_macro_qualified_parts(qualified, source)?;
20432    let arguments = call
20433        .child_by_field_name("arguments")
20434        .filter(|arguments| arguments.kind() == "argument_list")?;
20435    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20436    let semicolon = cpp_direct_semicolon(statement)?;
20437    if return_type.end_byte() > declared_name.start_byte()
20438        || macro_name.end_byte() > arguments.start_byte()
20439        || arguments.end_byte() > semicolon.start_byte()
20440    {
20441        return None;
20442    }
20443    Some(cpp_prototype_macro_candidate_from_parts(
20444        statement.start_byte(),
20445        macro_name,
20446        inner,
20447        arguments,
20448        semicolon,
20449    ))
20450}
20451
20452/// The inner parenthesized declarator of the `((parameters))` wrapper the
20453/// macro-declaration grammar makes of a prototype macro's argument list, with
20454/// the same span-keeping contract as [`cpp_prototype_macro_inner_arguments`]:
20455/// both paren pairs and their ownership come from the CST, and the returned
20456/// node's exact range is the one pair an ordinary function declarator needs.
20457fn cpp_prototype_macro_inner_parenthesized_declarator(outer: Node<'_>) -> Option<Node<'_>> {
20458    if outer.kind() != "parenthesized_declarator" || outer.named_child_count() != 1 {
20459        return None;
20460    }
20461    let open = outer.child(0)?;
20462    let close = outer.child(outer.child_count().checked_sub(1)?)?;
20463    if open.kind() != "(" || open.is_missing() || close.kind() != ")" || close.is_missing() {
20464        return None;
20465    }
20466    let inner = outer.named_child(0)?;
20467    if inner.kind() != "parenthesized_declarator" {
20468        return None;
20469    }
20470    let inner_open = inner.child(0)?;
20471    let inner_close = inner.child(inner.child_count().checked_sub(1)?)?;
20472    (inner_open.kind() == "("
20473        && !inner_open.is_missing()
20474        && inner_close.kind() == ")"
20475        && !inner_close.is_missing())
20476    .then_some(inner)
20477}
20478
20479/// Whether the macro-declaration grammar admitted a word of this declaration
20480/// as a `macro_modifier`, the structured proof that the declaration did not
20481/// parse without it.
20482fn cpp_declaration_has_macro_modifier(declaration: Node<'_>) -> bool {
20483    let mut cursor = declaration.walk();
20484    declaration
20485        .named_children(&mut cursor)
20486        .any(|child| child.kind() == "macro_modifier")
20487}
20488
20489/// `RET name _((args));` as the macro-declaration grammar reads it when the
20490/// argument list stays an expression: the return-type word is admitted as a
20491/// `macro_modifier`, the declared name takes the `type` field, and the macro
20492/// token is the init declarator's callee. Same #2932 contract as the
20493/// ERROR-based readers above, on nodes that carry no `ERROR` of their own.
20494fn cpp_prototype_macro_candidate_from_macro_modifier_init(
20495    declaration: Node<'_>,
20496    source: &str,
20497) -> Option<PrototypeMacroCandidate> {
20498    if !cpp_declaration_has_macro_modifier(declaration) {
20499        return None;
20500    }
20501    let declared_name = declaration
20502        .child_by_field_name("type")
20503        .filter(|type_node| type_node.kind() == "type_identifier")?;
20504    let init = declaration
20505        .child_by_field_name("declarator")
20506        .filter(|declarator| declarator.kind() == "init_declarator")?;
20507    let macro_name = init
20508        .child_by_field_name("declarator")
20509        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20510    let arguments = init
20511        .child_by_field_name("value")
20512        .filter(|value| value.kind() == "argument_list")?;
20513    let inner = cpp_prototype_macro_inner_arguments(arguments)?;
20514    let semicolon = cpp_direct_semicolon(declaration)?;
20515    if declared_name.end_byte() > macro_name.start_byte()
20516        || macro_name.end_byte() > arguments.start_byte()
20517        || arguments.end_byte() > semicolon.start_byte()
20518    {
20519        return None;
20520    }
20521    Some(cpp_prototype_macro_candidate_from_parts(
20522        declaration.start_byte(),
20523        macro_name,
20524        inner,
20525        arguments,
20526        semicolon,
20527    ))
20528}
20529
20530/// `RET *name _((args));` as the macro-declaration grammar reads it when the
20531/// argument list stays declarator-shaped: the declared name and the macro
20532/// token prefix the nested parenthesized declarator as the modifiers of a
20533/// `macro_modifier_declarator` chain.
20534fn cpp_prototype_macro_candidate_from_macro_modifier_declarator(
20535    declaration: Node<'_>,
20536    source: &str,
20537) -> Option<PrototypeMacroCandidate> {
20538    let mut declarator = declaration.child_by_field_name("declarator")?;
20539    while declarator.kind() == "pointer_declarator" {
20540        declarator = declarator.child_by_field_name("declarator")?;
20541    }
20542    // The macro's own modifier must be preceded by the declared name's.
20543    let mut saw_name_modifier = false;
20544    let (macro_name, arguments) = loop {
20545        if declarator.kind() != "macro_modifier_declarator" {
20546            return None;
20547        }
20548        let mut cursor = declarator.walk();
20549        let modifier = declarator
20550            .named_children(&mut cursor)
20551            .find(|child| child.kind() == "macro_modifier")
20552            .and_then(macro_modifier_identifier)?;
20553        let inner = declarator.child_by_field_name("declarator")?;
20554        if cpp_is_prototype_macro_identifier(modifier, source) {
20555            break (modifier, inner);
20556        }
20557        saw_name_modifier = true;
20558        declarator = inner;
20559    };
20560    if !saw_name_modifier {
20561        return None;
20562    }
20563    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20564    let semicolon = cpp_direct_semicolon(declaration)?;
20565    Some(cpp_prototype_macro_candidate_from_parts(
20566        declaration.start_byte(),
20567        macro_name,
20568        inner,
20569        arguments,
20570        semicolon,
20571    ))
20572}
20573
20574/// `RET name _((args));` as the macro-declaration grammar reads it when the
20575/// macro token is the one word that could stand as the type: the return-type
20576/// words and the declared name are `macro_modifier`s and the nested argument
20577/// list is the whole declarator.
20578fn cpp_prototype_macro_candidate_from_macro_modifier_type(
20579    declaration: Node<'_>,
20580    source: &str,
20581) -> Option<PrototypeMacroCandidate> {
20582    if !cpp_declaration_has_macro_modifier(declaration) {
20583        return None;
20584    }
20585    let macro_name = declaration
20586        .child_by_field_name("type")
20587        .filter(|type_node| cpp_is_prototype_macro_identifier(*type_node, source))?;
20588    let arguments = declaration.child_by_field_name("declarator")?;
20589    let inner = cpp_prototype_macro_inner_parenthesized_declarator(arguments)?;
20590    let semicolon = cpp_direct_semicolon(declaration)?;
20591    Some(cpp_prototype_macro_candidate_from_parts(
20592        declaration.start_byte(),
20593        macro_name,
20594        inner,
20595        arguments,
20596        semicolon,
20597    ))
20598}
20599
20600/// `RET name _((args));` as the macro-declaration grammar reads it when the
20601/// argument list itself needs recovery: the return-type word is a
20602/// `macro_modifier`, the declared name takes the `type` field, the macro token
20603/// is the first declarator, and the argument list's paren pairs land in the
20604/// bracketing `ERROR`s.
20605fn cpp_prototype_macro_candidate_from_macro_modifier_flat(
20606    declaration: Node<'_>,
20607    source: &str,
20608) -> Option<PrototypeMacroCandidate> {
20609    if !cpp_declaration_has_macro_modifier(declaration) {
20610        return None;
20611    }
20612    let declared_name = declaration
20613        .child_by_field_name("type")
20614        .filter(|type_node| type_node.kind() == "type_identifier")?;
20615    let mut declarator_cursor = declaration.walk();
20616    let macro_name = declaration
20617        .children_by_field_name("declarator", &mut declarator_cursor)
20618        .next()
20619        .filter(|declarator| cpp_is_prototype_macro_identifier(*declarator, source))?;
20620    let open_error = macro_name
20621        .next_named_sibling()
20622        .filter(|next| next.kind() == "ERROR")?;
20623    let close_error = last_named_child(declaration)
20624        .filter(|last| last.kind() == "ERROR" && !same_node(*last, open_error))?;
20625    if open_error.child_count() < 3
20626        || open_error
20627            .child(0)
20628            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20629        || open_error
20630            .child(1)
20631            .is_none_or(|open| open.kind() != "(" || open.is_missing())
20632        || close_error.child_count() != 2
20633        || close_error
20634            .child(0)
20635            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20636        || close_error
20637            .child(1)
20638            .is_none_or(|close| close.kind() != ")" || close.is_missing())
20639    {
20640        return None;
20641    }
20642    let inner_open = open_error.child(1)?;
20643    let inner_close = close_error.child(0)?;
20644    let outer_close = close_error.child(1)?;
20645    let semicolon = cpp_direct_semicolon(declaration)?;
20646    if declared_name.end_byte() > macro_name.start_byte()
20647        || macro_name.end_byte() > open_error.start_byte()
20648        || inner_open.start_byte() > inner_close.end_byte()
20649        || inner_close.end_byte() > outer_close.start_byte()
20650        || outer_close.end_byte() > semicolon.start_byte()
20651    {
20652        return None;
20653    }
20654    Some(PrototypeMacroCandidate {
20655        run_start: declaration.start_byte(),
20656        identifier_start: macro_name.start_byte(),
20657        inner_open_start: inner_open.start_byte(),
20658        inner_close_end: inner_close.end_byte(),
20659        outer_close_end: outer_close.end_byte(),
20660        semicolon_end: semicolon.end_byte(),
20661    })
20662}
20663
20664fn cpp_prototype_macro_candidate_from_parts(
20665    run_start: usize,
20666    macro_name: Node<'_>,
20667    inner_arguments: Node<'_>,
20668    outer_arguments: Node<'_>,
20669    semicolon: Node<'_>,
20670) -> PrototypeMacroCandidate {
20671    PrototypeMacroCandidate {
20672        run_start,
20673        identifier_start: macro_name.start_byte(),
20674        inner_open_start: inner_arguments.start_byte(),
20675        inner_close_end: inner_arguments.end_byte(),
20676        outer_close_end: outer_arguments.end_byte(),
20677        semicolon_end: semicolon.end_byte(),
20678    }
20679}
20680
20681/// Recover only CST shapes whose fields preserve the declaration name, macro
20682/// invocation, nested argument list, and terminator. Arbitrary flattened
20683/// `ERROR` wreckage stays unsupported rather than being interpreted through a
20684/// token or delimiter scan (issue #2932).
20685fn cpp_prototype_macro_candidates(node: Node<'_>, source: &str) -> Vec<PrototypeMacroCandidate> {
20686    let candidate = match node.kind() {
20687        "declaration" | "field_declaration" => {
20688            cpp_prototype_macro_candidate_from_init_declaration(node, source)
20689                .or_else(|| cpp_prototype_macro_candidate_from_qualified_declaration(node, source))
20690                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_init(node, source))
20691                .or_else(|| {
20692                    cpp_prototype_macro_candidate_from_macro_modifier_declarator(node, source)
20693                })
20694                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_type(node, source))
20695                .or_else(|| cpp_prototype_macro_candidate_from_macro_modifier_flat(node, source))
20696        }
20697        "expression_statement" => {
20698            cpp_prototype_macro_candidate_from_pointer_expression(node, source)
20699        }
20700        _ => None,
20701    };
20702    candidate.into_iter().collect()
20703}
20704
20705fn cpp_macro_swallowed_declaration_envelope(node: Node<'_>, source: &str) -> bool {
20706    if !node.has_error() || !matches!(node.kind(), "ERROR" | "function_definition") {
20707        return false;
20708    }
20709    if node.kind() == "function_definition" && node.child_by_field_name("type").is_some() {
20710        return false;
20711    }
20712    let Some(declarator) = (if node.kind() == "function_definition" {
20713        node.child_by_field_name("declarator")
20714            .and_then(extract_function_declarator)
20715    } else {
20716        node.named_child(0)
20717            .filter(|child| child.kind() == "function_declarator")
20718    }) else {
20719        return false;
20720    };
20721    let Some(name) = cpp_function_declarator_name_node(declarator) else {
20722        return false;
20723    };
20724    declarator.start_byte() == node.start_byte()
20725        && name.kind() == "identifier"
20726        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
20727}
20728
20729/// Reparse a fragmented class-body interior while preserving its original byte
20730/// and line offsets, confined to the region by tree-sitter included ranges.
20731///
20732/// This used to materialize the region's whole file prefix as whitespace and
20733/// make the lexer walk it, the technique #1309 replaced on the other reparse
20734/// path: O(file) per fragmented-class recovery, on files that are already
20735/// error-recovered and already slow (#2788). Included ranges give the parser
20736/// the same view -- the region's bytes, at their original offsets and
20737/// line/column positions -- without materializing or lexing anything before it.
20738///
20739/// The equality of the two views is the claim, so a debug build parses both and
20740/// asserts the trees agree node for node.
20741fn cpp_reparse_fragmented_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20742    let region = cpp_reparse_region_items(source, start, end);
20743
20744    #[cfg(debug_assertions)]
20745    assert_eq!(
20746        region.as_ref().map(cpp_tree_shape),
20747        cpp_reparse_padded_class_body(source, start, end)
20748            .as_ref()
20749            .map(cpp_tree_shape),
20750        "the region reparse of [{start}, {end}) must be the parse a whitespace-padded \
20751         prefix produces"
20752    );
20753
20754    region
20755}
20756
20757/// The whitespace-padded reparse [`cpp_reparse_fragmented_class_body`]
20758/// replaces, kept as the oracle a debug build asserts every region reparse
20759/// against and as the release-mode parity tests' reference (#2788).
20760#[cfg(any(debug_assertions, test))]
20761fn cpp_reparse_padded_class_body(source: &str, start: usize, end: usize) -> Option<Tree> {
20762    if start >= end {
20763        // The included-range parser refuses an empty region; the padded one
20764        // returned a tree holding nothing, which every caller read as "no
20765        // members here".
20766        return None;
20767    }
20768    let bytes = source.as_bytes();
20769    let prefix = bytes.get(..start)?;
20770    let interior = bytes.get(start..end)?;
20771    let mut padded = Vec::with_capacity(end);
20772    padded.extend(
20773        prefix
20774            .iter()
20775            .map(|&byte| if byte == b'\n' { b'\n' } else { b' ' }),
20776    );
20777    padded.extend_from_slice(interior);
20778    let padded = String::from_utf8(padded).ok()?;
20779    let mut parser = tree_sitter::Parser::new();
20780    parser
20781        .set_language(&tree_sitter_cpp::LANGUAGE.into())
20782        .ok()?;
20783    parser.parse(&padded, None)
20784}
20785
20786/// Every node of `tree` in preorder, by kind, span and position, which is what
20787/// two reparses of one region have to agree on for their callers to read the
20788/// same declarations out of either (#2788).
20789#[cfg(any(debug_assertions, test))]
20790fn cpp_tree_shape(tree: &Tree) -> Vec<(&'static str, usize, usize, usize, usize, bool, bool)> {
20791    let mut shape = Vec::new();
20792    let mut cursor = tree.root_node().walk();
20793    let mut stack = vec![tree.root_node()];
20794    while let Some(node) = stack.pop() {
20795        shape.push((
20796            node.kind(),
20797            node.start_byte(),
20798            node.end_byte(),
20799            node.start_position().row,
20800            node.start_position().column,
20801            node.is_named(),
20802            node.is_missing(),
20803        ));
20804        let children: Vec<Node<'_>> = node.children(&mut cursor).collect();
20805        stack.extend(children.into_iter().rev());
20806    }
20807    shape
20808}
20809
20810/// Robustness gate adapting #1015's `rust_reparsed_items_are_indexable`: the
20811/// reparsed interior is indexed only when every top-level named node is a
20812/// well-formed C++ item (or a comment) and at least one real item is present.
20813/// Expression/statement soup surfaces as a top-level `ERROR` or
20814/// `expression_statement`, neither of which is an item kind, so it is rejected.
20815///
20816/// Unlike the Rust gate, this does NOT reject on `root.has_error()`: a nested
20817/// begin/end sentinel inside the region (e.g. `namespace outer { BEGIN_NS ...`
20818/// swallowed by a preceding dangling sentinel) reparses to a real
20819/// `namespace_definition` whose body still holds a bogus `function_definition`,
20820/// so the subtree legitimately carries an error. Container items are admitted
20821/// even with an internal error; the inner bogus function is recovered recursively
20822/// when `visit_function_definition` walks it. Each recursion strips at least one
20823/// leading sentinel, so the region strictly shrinks and recovery terminates.
20824///
20825/// A top-level `function_definition` is the one place we stay strict: it is
20826/// admitted only when it is clean or is itself a sentinel candidate. A function
20827/// that has an error and is not a sentinel is a real callable with a broken body,
20828/// so we refuse the whole reparse and let the ordinary path handle it (preserving
20829/// its real return type rather than re-deriving an implicit one).
20830fn cpp_reparsed_items_are_indexable(root: Node<'_>, source: &str) -> bool {
20831    let mut cursor = root.walk();
20832    let mut saw_item = false;
20833    for child in root.named_children(&mut cursor) {
20834        match child.kind() {
20835            "comment" => {}
20836            "function_definition" => {
20837                if child.has_error() && cpp_sentinel_macro_region(child, source).is_none() {
20838                    return false;
20839                }
20840                saw_item = true;
20841            }
20842            kind if cpp_is_indexable_item_kind(kind) => saw_item = true,
20843            _ => return false,
20844        }
20845    }
20846    saw_item
20847}
20848
20849/// Robustness gate for a reparsed fragmented multiple-base export class body
20850/// (issue #938). Adapts `cpp_reparsed_items_are_indexable` to the member-shaped
20851/// kinds a class body produces when reparsed at translation-unit scope: the
20852/// access-specifier label preceding the first member surfaces as a
20853/// `labeled_statement` wrapping that member, and members surface as
20854/// `declaration`/`field_declaration`/`function_definition`/nested type specifiers.
20855/// Statement or expression soup surfaces as other top-level kinds and is rejected,
20856/// so only a genuinely member-shaped body is ever re-owned as members; anything
20857/// ambiguous falls back to indexing the class alone.
20858fn cpp_reparsed_member_error_is_indexable(node: Node<'_>) -> bool {
20859    if node.kind() != "ERROR" {
20860        return false;
20861    }
20862    let mut stack = Vec::new();
20863    let mut saw_function_declarator = false;
20864    let mut cursor = node.walk();
20865    for child in node.named_children(&mut cursor) {
20866        stack.push(child);
20867    }
20868    while let Some(current) = stack.pop() {
20869        match current.kind() {
20870            // Tree-sitter may wrap adjacent copy-control declarations in a
20871            // nested ERROR. Keep descending only through ERROR wrappers; the
20872            // actual declaration payload must be a function_declarator.
20873            "ERROR" => {
20874                let mut cursor = current.walk();
20875                stack.extend(current.named_children(&mut cursor));
20876            }
20877            "function_declarator" => saw_function_declarator = true,
20878            _ => return false,
20879        }
20880    }
20881    saw_function_declarator
20882}
20883
20884fn cpp_reparsed_adjacent_copy_control_error(node: Node<'_>, source: &str) -> bool {
20885    if node.kind() != "ERROR" {
20886        return false;
20887    }
20888    let mut cursor = node.walk();
20889    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
20890    let [explicit, constructor_error, tail] = named.as_slice() else {
20891        return false;
20892    };
20893    if explicit.kind() != "explicit_function_specifier" || constructor_error.kind() != "ERROR" {
20894        return false;
20895    }
20896    // The copy-control declarators the inner ERROR holds, with only their `;`
20897    // tokens beside them. The older grammar kept the constructor alone there
20898    // and gave the destructor the slot after it; the macro-declaration grammar
20899    // absorbs the destructor beside the constructor and folds the member
20900    // written after them into the tail slot as a `macro_modifier_declarator`.
20901    let mut declarators = Vec::new();
20902    let mut stack = vec![*constructor_error];
20903    while let Some(current) = stack.pop() {
20904        match current.kind() {
20905            "ERROR" => {
20906                let mut cursor = current.walk();
20907                let children = current.named_children(&mut cursor).collect::<Vec<_>>();
20908                stack.extend(children.into_iter().rev());
20909            }
20910            "function_declarator" => declarators.push(current),
20911            _ => return false,
20912        }
20913    }
20914    let (constructor, destructor) = match (declarators.as_slice(), tail.kind()) {
20915        ([constructor], "function_declarator") => (*constructor, *tail),
20916        ([constructor, destructor], "macro_modifier_declarator") => (*constructor, *destructor),
20917        _ => return false,
20918    };
20919    let Some(constructor_name) =
20920        extract_function_declarator(constructor).and_then(cpp_function_declarator_name_node)
20921    else {
20922        return false;
20923    };
20924    let Some(destructor_name) =
20925        extract_function_declarator(destructor).and_then(cpp_function_declarator_name_node)
20926    else {
20927        return false;
20928    };
20929    let Some(destroyed_type) = destructor_name.named_child(0) else {
20930        return false;
20931    };
20932    constructor_name.kind() == "identifier"
20933        && destructor_name.kind() == "destructor_name"
20934        && destroyed_type.kind() == "identifier"
20935        && node_text(constructor_name, source) == node_text(destroyed_type, source)
20936}
20937
20938fn cpp_reparsed_constructor_body_is_indexable(node: Node<'_>, source: &str) -> bool {
20939    if node.kind() != "compound_statement" {
20940        return false;
20941    }
20942    let Some(prefix) = cpp_prev_non_comment_named_sibling(node) else {
20943        return false;
20944    };
20945    if prefix.kind() == "labeled_statement"
20946        && prefix.named_child(0).is_some_and(|label| {
20947            matches!(
20948                node_text(label, source).trim(),
20949                "public" | "private" | "protected"
20950            )
20951        })
20952    {
20953        return prefix.named_children(&mut prefix.walk()).any(|child| {
20954            child.kind() == "declaration"
20955                && child.has_error()
20956                && child
20957                    .named_children(&mut child.walk())
20958                    .any(cpp_reparsed_member_error_is_indexable)
20959        });
20960    }
20961    // A malformed constructor initializer can be split into a declaration
20962    // followed by its compound body when the class prefix already contains
20963    // realistic members. Keep this admission tied to that exact structured
20964    // declaration/error/body chain rather than accepting arbitrary blocks.
20965    prefix.kind() == "declaration"
20966        && prefix.has_error()
20967        && prefix
20968            .named_children(&mut prefix.walk())
20969            .any(|child| child.kind() == "ERROR" && cpp_reparsed_member_error_is_indexable(child))
20970}
20971
20972fn cpp_reparsed_member_error_with_preprocessed_body(node: Node<'_>) -> bool {
20973    if !cpp_reparsed_member_error_is_indexable(node) {
20974        return false;
20975    }
20976    let Some(preproc) = node.next_named_sibling() else {
20977        return false;
20978    };
20979    preproc.kind() == "preproc_if"
20980        && preproc.has_error()
20981        && preproc
20982            .named_children(&mut preproc.walk())
20983            .any(|child| child.kind() == "expression_statement" && child.has_error())
20984        && preproc
20985            .next_named_sibling()
20986            .is_some_and(|body| body.kind() == "compound_statement")
20987}
20988
20989/// Return a function body whose braces and ownership are explicit in the
20990/// reparsed class-member tree. An error below a real function envelope is
20991/// recoverable by the ordinary function visitor; a missing/deferred body is
20992/// not, because accepting it would let statement soup masquerade as a member.
20993fn cpp_reparsed_member_function_body(node: Node<'_>) -> Option<Node<'_>> {
20994    if node.kind() != "function_definition" {
20995        return None;
20996    }
20997    let body = node.child_by_field_name("body")?;
20998    if body.kind() != "compound_statement" {
20999        return None;
21000    }
21001    let open = body.child(0)?;
21002    let close = body.child(body.child_count().checked_sub(1)?)?;
21003    if open.kind() != "{"
21004        || open.is_missing()
21005        || close.kind() != "}"
21006        || close.is_missing()
21007        || close.end_byte() != body.end_byte()
21008        || body.end_byte() != node.end_byte()
21009    {
21010        return None;
21011    }
21012    Some(body)
21013}
21014
21015fn cpp_reparsed_member_function_errors_are_in_body(
21016    node: Node<'_>,
21017    body: Node<'_>,
21018    source: &str,
21019) -> bool {
21020    let mut cursor = node.walk();
21021    node.children(&mut cursor).all(|child| {
21022        same_node(child, body)
21023            || cpp_reparsed_member_attribute_error(child, source)
21024            || cpp_reparsed_member_signature_identifier_errors(child)
21025            || (!child.has_error() && !child.is_error() && !child.is_missing())
21026    })
21027}
21028
21029/// A complete callable can still carry parser errors in its signature when a
21030/// project annotation is not part of the C++ grammar (`nonneg int`,
21031/// `RET_NONNULL`, or a constraint macro argument). Such annotations surface as
21032/// empty ERROR nodes or ERROR nodes containing identifiers. Admit only those
21033/// leaves inside the already-proven callable envelope; structured statements,
21034/// literals, missing tokens, and other malformed signature payload remain
21035/// rejected.
21036fn cpp_reparsed_member_signature_identifier_errors(node: Node<'_>) -> bool {
21037    if !node.has_error() && !node.is_error() && !node.is_missing() {
21038        return false;
21039    }
21040    let mut stack = vec![node];
21041    let mut saw_error = false;
21042    while let Some(current) = stack.pop() {
21043        if current.is_missing() {
21044            return false;
21045        }
21046        if current.kind() == "ERROR" {
21047            saw_error = true;
21048            let mut cursor = current.walk();
21049            let children = current.named_children(&mut cursor).collect::<Vec<_>>();
21050            if children
21051                .iter()
21052                .any(|child| !matches!(child.kind(), "ERROR" | "identifier"))
21053            {
21054                return false;
21055            }
21056            stack.extend(children);
21057            continue;
21058        }
21059        let mut cursor = current.walk();
21060        stack.extend(current.children(&mut cursor));
21061    }
21062    saw_error
21063}
21064
21065fn cpp_reparsed_member_attribute_error(node: Node<'_>, source: &str) -> bool {
21066    node.kind() == "ERROR"
21067        && node.named_child_count() == 1
21068        && node
21069            .named_child(0)
21070            .is_some_and(|attribute| cpp_reparsed_export_macro_identifier(attribute, source))
21071}
21072
21073fn cpp_reparsed_export_macro_identifier(node: Node<'_>, source: &str) -> bool {
21074    node.kind() == "identifier"
21075        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
21076}
21077
21078/// A C++ attribute placed between a member's declarator and body can make
21079/// tree-sitter expose the callable as
21080/// `type ERROR(init_declarator(name, argument_list)) ATTRIBUTE { ... }`.
21081/// Keep this admission tied to that exact node geometry. In particular, an
21082/// arbitrary ERROR or identifier before a compound statement is not enough.
21083fn cpp_reparsed_attribute_member_function(node: Node<'_>, source: &str) -> bool {
21084    let Some(body) = cpp_reparsed_member_function_body(node) else {
21085        return false;
21086    };
21087    let mut cursor = node.walk();
21088    let named = node
21089        .named_children(&mut cursor)
21090        .filter(|child| child.kind() != "comment")
21091        .collect::<Vec<_>>();
21092    let [type_node, error, attribute, body_node] = named.as_slice() else {
21093        return false;
21094    };
21095    if !same_node(*body_node, body)
21096        || !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
21097        || !cpp_reparsed_export_macro_identifier(*attribute, source)
21098        || error.kind() != "ERROR"
21099        || error.named_child_count() != 1
21100    {
21101        return false;
21102    }
21103    error
21104        .named_child(0)
21105        .is_some_and(cpp_reparsed_attribute_callable_declarator)
21106}
21107
21108fn cpp_reparsed_member_return_type_is_indexable(node: Node<'_>, source: &str) -> bool {
21109    cpp_structured_type_path(node, source).is_some()
21110        && !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(node, source)))
21111}
21112
21113fn cpp_reparsed_member_return_and_declarator_are_indexable(
21114    return_error: Node<'_>,
21115    declarator: Node<'_>,
21116    body_node: Node<'_>,
21117    body: Node<'_>,
21118    source: &str,
21119) -> bool {
21120    return_error.kind() == "ERROR"
21121        && return_error.named_child_count() == 1
21122        && same_node(body_node, body)
21123        && return_error.named_child(0).is_some_and(|return_type| {
21124            cpp_reparsed_member_return_type_is_indexable(return_type, source)
21125        })
21126        && extract_function_declarator(declarator)
21127            .and_then(cpp_function_declarator_name_node)
21128            .is_some()
21129}
21130
21131fn cpp_reparsed_friend_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21132    let Some(body) = cpp_reparsed_member_function_body(node) else {
21133        return false;
21134    };
21135    let mut cursor = node.walk();
21136    let named = node
21137        .named_children(&mut cursor)
21138        .filter(|child| child.kind() != "comment")
21139        .collect::<Vec<_>>();
21140    let [friend, return_error, declarator, body_node] = named.as_slice() else {
21141        return false;
21142    };
21143    friend.kind() == "type_identifier"
21144        && node_text(*friend, source) == "friend"
21145        && cpp_reparsed_member_return_and_declarator_are_indexable(
21146            *return_error,
21147            *declarator,
21148            *body_node,
21149            body,
21150            source,
21151        )
21152}
21153
21154fn cpp_reparsed_prefix_attribute_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21155    let Some(body) = cpp_reparsed_member_function_body(node) else {
21156        return false;
21157    };
21158    let mut cursor = node.walk();
21159    let named = node
21160        .named_children(&mut cursor)
21161        .filter(|child| child.kind() != "comment")
21162        .collect::<Vec<_>>();
21163    let [prefix @ .., attribute, return_error, declarator, body_node] = named.as_slice() else {
21164        return false;
21165    };
21166    prefix
21167        .iter()
21168        .all(|node| matches!(node.kind(), "storage_class_specifier" | "type_qualifier"))
21169        && attribute.kind() == "type_identifier"
21170        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
21171        && cpp_reparsed_member_return_and_declarator_are_indexable(
21172            *return_error,
21173            *declarator,
21174            *body_node,
21175            body,
21176            source,
21177        )
21178}
21179
21180/// An included-range reparse that begins inside a malformed class can merge an
21181/// access label and following template member. Tree-sitter then emits the label
21182/// as the `template_type` name, the template parameter list as its arguments,
21183/// an ERROR-wrapped return type, the callable declarator, and its complete
21184/// body. Admit only that exact structured displacement.
21185fn cpp_reparsed_access_template_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21186    let Some(body) = cpp_reparsed_member_function_body(node) else {
21187        return false;
21188    };
21189    let mut cursor = node.walk();
21190    let named = node
21191        .named_children(&mut cursor)
21192        .filter(|child| child.kind() != "comment")
21193        .collect::<Vec<_>>();
21194    // The member's return type is an `ERROR` of its own, or -- under the
21195    // macro-declaration grammar, which admits it as a modifier of the
21196    // declarator it could not separate -- folded into the declarator's
21197    // `macro_modifier_declarator` wrapper.
21198    let (template_type, return_part, declarator, body_node) = match named.as_slice() {
21199        [template_type, return_error, declarator, body_node] => {
21200            let Some(return_type) = return_error.named_child(0) else {
21201                return false;
21202            };
21203            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21204                return false;
21205            }
21206            (*template_type, return_type, *declarator, *body_node)
21207        }
21208        [template_type, declarator, body_node] => {
21209            let Some(return_type) = (declarator.kind() == "macro_modifier_declarator")
21210                .then(|| {
21211                    let mut cursor = declarator.walk();
21212                    declarator
21213                        .named_children(&mut cursor)
21214                        .find(|child| child.kind() == "macro_modifier")
21215                })
21216                .flatten()
21217                .and_then(macro_modifier_identifier)
21218            else {
21219                return false;
21220            };
21221            let Some(function) = declarator.child_by_field_name("declarator") else {
21222                return false;
21223            };
21224            (*template_type, return_type, function, *body_node)
21225        }
21226        _ => return false,
21227    };
21228    let Some(template_name) = template_type.child_by_field_name("name") else {
21229        return false;
21230    };
21231    let Some(arguments) = template_type.child_by_field_name("arguments") else {
21232        return false;
21233    };
21234    let mut cursor = template_type.walk();
21235    let template_errors = template_type
21236        .named_children(&mut cursor)
21237        .filter(|child| child.kind() == "ERROR")
21238        .collect::<Vec<_>>();
21239    let [comment_error] = template_errors.as_slice() else {
21240        return false;
21241    };
21242    let mut cursor = comment_error.walk();
21243    let error_children = comment_error.children(&mut cursor).collect::<Vec<_>>();
21244    let [colon, comments @ .., template_keyword] = error_children.as_slice() else {
21245        return false;
21246    };
21247    same_node(body_node, body)
21248        && template_type.kind() == "template_type"
21249        && template_name.kind() == "type_identifier"
21250        && matches!(
21251            node_text(template_name, source).trim(),
21252            "public" | "private" | "protected"
21253        )
21254        && arguments.kind() == "template_argument_list"
21255        && arguments.named_child_count() > 0
21256        && !arguments.has_error()
21257        && !colon.is_named()
21258        && colon.kind() == ":"
21259        && comments.iter().all(|child| child.kind() == "comment")
21260        && !template_keyword.is_named()
21261        && template_keyword.kind() == "template"
21262        && cpp_reparsed_member_return_type_is_indexable(return_part, source)
21263        && extract_function_declarator(declarator)
21264            .and_then(cpp_function_declarator_name_node)
21265            .is_some()
21266}
21267
21268/// Return the constructor declaration tree-sitter can merge into an access
21269/// label when a class-body reparse begins immediately before `#if`, `#ifdef`,
21270/// or `#ifndef`. The conditional token and macro name become an ERROR plus the
21271/// declaration's apparent type; the callable name must still exactly match the
21272/// recovered class, so unrelated labeled statements are never re-owned.
21273fn cpp_reparsed_preprocessor_constructor<'tree>(
21274    node: Node<'tree>,
21275    class_name: &str,
21276    source: &str,
21277) -> Option<Node<'tree>> {
21278    if node.kind() != "labeled_statement" {
21279        return None;
21280    }
21281    let mut cursor = node.walk();
21282    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21283    let [label, directive_error, declaration] = named.as_slice() else {
21284        return None;
21285    };
21286    if label.kind() != "statement_identifier"
21287        || !matches!(
21288            node_text(*label, source),
21289            "public" | "private" | "protected"
21290        )
21291        || directive_error.kind() != "ERROR"
21292        || directive_error.child_count() != 1
21293        || directive_error
21294            .child(0)
21295            .is_none_or(|directive| !matches!(directive.kind(), "#if" | "#ifdef" | "#ifndef"))
21296        || declaration.kind() != "declaration"
21297        || declaration.named_child_count() != 2
21298    {
21299        return None;
21300    }
21301    let apparent_type = declaration.child_by_field_name("type")?;
21302    if apparent_type.kind() != "type_identifier"
21303        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(apparent_type, source)))
21304    {
21305        return None;
21306    }
21307    let declarator = declaration.child_by_field_name("declarator")?;
21308    let function = extract_function_declarator(declarator)?;
21309    let name = cpp_function_declarator_name_node(function)?;
21310    (node_text(name, source) == class_name).then_some(*declaration)
21311}
21312
21313fn cpp_reparsed_attribute_callable_declarator(node: Node<'_>) -> bool {
21314    if extract_function_declarator(node)
21315        .and_then(cpp_function_declarator_name_node)
21316        .is_some()
21317    {
21318        return true;
21319    }
21320    node.kind() == "init_declarator"
21321        && node
21322            .child_by_field_name("declarator")
21323            .is_some_and(|declarator| declarator.kind() == "identifier")
21324        && node
21325            .child_by_field_name("value")
21326            .is_some_and(|value| value.kind() == "argument_list" && value.named_child_count() == 0)
21327}
21328
21329/// Return true for the constrained/attribute form that tree-sitter splits into
21330/// an ERROR declaration, a preprocessor `requires` clause, and a following
21331/// compound statement. The three nodes must remain immediate named siblings;
21332/// this deliberately does not search source text or skip unrelated statements.
21333fn cpp_reparsed_attribute_requires_error(node: Node<'_>, source: &str) -> bool {
21334    if node.kind() != "ERROR" || node.named_child_count() != 3 {
21335        return false;
21336    }
21337    let mut cursor = node.walk();
21338    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21339    let [type_node, function_declarator, attribute] = named.as_slice() else {
21340        return false;
21341    };
21342    if !cpp_reparsed_member_return_type_is_indexable(*type_node, source)
21343        || !cpp_reparsed_attribute_callable_declarator(*function_declarator)
21344        || attribute.kind() != "identifier"
21345        || !cpp_export_macro_token(&normalize_cpp_whitespace(node_text(*attribute, source)))
21346    {
21347        return false;
21348    }
21349    let Some(preproc) =
21350        cpp_next_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
21351    else {
21352        return false;
21353    };
21354    let Some(body) = cpp_next_non_comment_named_sibling(preproc)
21355        .filter(|sibling| sibling.kind() == "compound_statement")
21356    else {
21357        return false;
21358    };
21359    let Some(open) = body.child(0) else {
21360        return false;
21361    };
21362    let Some(close) = body.child(body.child_count().saturating_sub(1)) else {
21363        return false;
21364    };
21365    let Some(condition) = preproc.child_by_field_name("condition") else {
21366        return false;
21367    };
21368    let mut cursor = preproc.walk();
21369    let payload = preproc
21370        .named_children(&mut cursor)
21371        .filter(|child| child.kind() != "comment" && !same_node(*child, condition))
21372        .collect::<Vec<_>>();
21373    let [requires_statement] = payload.as_slice() else {
21374        return false;
21375    };
21376    let requires_clause = requires_statement.named_child(0);
21377
21378    open.kind() == "{"
21379        && !open.is_missing()
21380        && close.kind() == "}"
21381        && !close.is_missing()
21382        && close.end_byte() == body.end_byte()
21383        && requires_statement.kind() == "expression_statement"
21384        && requires_statement.named_child_count() == 1
21385        && requires_clause.is_some_and(|clause| clause.kind() == "requires_clause")
21386}
21387
21388fn cpp_next_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
21389    let mut sibling = node.next_named_sibling();
21390    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
21391        sibling = sibling.and_then(|candidate| candidate.next_named_sibling());
21392    }
21393    sibling
21394}
21395
21396fn cpp_prev_non_comment_named_sibling(node: Node<'_>) -> Option<Node<'_>> {
21397    let mut sibling = node.prev_named_sibling();
21398    while sibling.is_some_and(|candidate| candidate.kind() == "comment") {
21399        sibling = sibling.and_then(|candidate| candidate.prev_named_sibling());
21400    }
21401    sibling
21402}
21403
21404fn cpp_reparsed_attribute_requires_body(node: Node<'_>, source: &str) -> bool {
21405    let Some(preproc) =
21406        cpp_prev_non_comment_named_sibling(node).filter(|sibling| sibling.kind() == "preproc_if")
21407    else {
21408        return false;
21409    };
21410    let Some(error) =
21411        cpp_prev_non_comment_named_sibling(preproc).filter(|sibling| sibling.kind() == "ERROR")
21412    else {
21413        return false;
21414    };
21415    cpp_reparsed_attribute_requires_error(error, source)
21416}
21417
21418fn cpp_reparsed_template_macro_prefix_parameter_name<'tree>(
21419    parameter: Node<'tree>,
21420    macro_name: Node<'tree>,
21421    message: Node<'tree>,
21422    source: &str,
21423) -> Option<Node<'tree>> {
21424    let parameter_name = parameter.named_child(0)?;
21425    (parameter.kind() == "type_parameter_declaration"
21426        && parameter_name.kind() == "type_identifier"
21427        && matches!(macro_name.kind(), "identifier" | "type_identifier")
21428        && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(macro_name, source)))
21429        && message.kind() == "string_literal")
21430        .then_some(parameter_name)
21431}
21432
21433fn cpp_reparsed_template_macro_prefix_parameter<'tree>(
21434    node: Node<'tree>,
21435    source: &str,
21436) -> Option<Node<'tree>> {
21437    if node.kind() != "ERROR" {
21438        return None;
21439    }
21440    let mut cursor = node.walk();
21441    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21442    let [parameter, macro_name, message] = named.as_slice() else {
21443        return None;
21444    };
21445    cpp_reparsed_template_macro_prefix_parameter_name(*parameter, *macro_name, *message, source)
21446}
21447
21448/// Recognize the alternate constraint-macro prefix where tree-sitter retains
21449/// the complete qualified constraint as a fourth child instead of moving it
21450/// into the following function. Keep the gate tied to a two-type template
21451/// constraint that names the declared type parameter.
21452fn cpp_reparsed_template_macro_constraint_prefix_parameter<'tree>(
21453    node: Node<'tree>,
21454    source: &str,
21455) -> Option<Node<'tree>> {
21456    if node.kind() != "ERROR" {
21457        return None;
21458    }
21459    let mut cursor = node.walk();
21460    let named = node.named_children(&mut cursor).collect::<Vec<_>>();
21461    let [parameter, macro_name, message, constraint] = named.as_slice() else {
21462        return None;
21463    };
21464    let parameter_name = cpp_reparsed_template_macro_prefix_parameter_name(
21465        *parameter,
21466        *macro_name,
21467        *message,
21468        source,
21469    )?;
21470    let constraint_scope = constraint.child_by_field_name("scope")?;
21471    let constraint_template = constraint.child_by_field_name("name")?;
21472    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21473    let mut argument_cursor = constraint_arguments.walk();
21474    let constraint_types = constraint_arguments
21475        .named_children(&mut argument_cursor)
21476        .collect::<Vec<_>>();
21477    if constraint.kind() != "qualified_identifier"
21478        || constraint_scope.kind() != "namespace_identifier"
21479        || !matches!(
21480            constraint_template.kind(),
21481            "template_function" | "template_type"
21482        )
21483        || !matches!(constraint_types.as_slice(), [left, right]
21484            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21485        || constraint_arguments.has_error()
21486    {
21487        return None;
21488    }
21489    let parameter_text = node_text(parameter_name, source);
21490    let mut stack = constraint_types;
21491    while let Some(current) = stack.pop() {
21492        if current.kind() == "type_identifier" && node_text(current, source) == parameter_text {
21493            return Some(parameter_name);
21494        }
21495        let mut cursor = current.walk();
21496        stack.extend(current.named_children(&mut cursor));
21497    }
21498    None
21499}
21500
21501fn cpp_reparsed_template_macro_companion_is_indexable(
21502    node: Node<'_>,
21503    parameter_name: Node<'_>,
21504    source: &str,
21505) -> bool {
21506    let Some(body) = cpp_reparsed_member_function_body(node) else {
21507        return false;
21508    };
21509    let mut cursor = node.walk();
21510    let named = node
21511        .named_children(&mut cursor)
21512        .filter(|child| child.kind() != "comment")
21513        .collect::<Vec<_>>();
21514    // The member's return type is an `ERROR` of its own, or -- under the
21515    // macro-declaration grammar, which admits it as a modifier of the
21516    // declarator it could not separate -- folded into the declarator's
21517    // `macro_modifier_declarator` wrapper.
21518    let (constraint, close_error, storage, return_type, declarator, body_node) = match named
21519        .as_slice()
21520    {
21521        [
21522            constraint,
21523            close_error,
21524            storage,
21525            return_error,
21526            declarator,
21527            body_node,
21528        ] => {
21529            let Some(return_type) = return_error.named_child(0) else {
21530                return false;
21531            };
21532            if return_error.kind() != "ERROR" || return_error.named_child_count() != 1 {
21533                return false;
21534            }
21535            (
21536                *constraint,
21537                *close_error,
21538                *storage,
21539                return_type,
21540                *declarator,
21541                *body_node,
21542            )
21543        }
21544        [constraint, close_error, storage, declarator, body_node] => {
21545            let Some((return_type, function)) = macro_modifier_declarator_parts(*declarator) else {
21546                return false;
21547            };
21548            (
21549                *constraint,
21550                *close_error,
21551                *storage,
21552                return_type,
21553                function,
21554                *body_node,
21555            )
21556        }
21557        _ => return false,
21558    };
21559    let Some(constraint_scope) = constraint.child_by_field_name("scope") else {
21560        return false;
21561    };
21562    let Some(constraint_template) = constraint.child_by_field_name("name") else {
21563        return false;
21564    };
21565    let Some(constraint_arguments) = constraint_template.child_by_field_name("arguments") else {
21566        return false;
21567    };
21568    let mut cursor = constraint_arguments.walk();
21569    let constraint_types = constraint_arguments
21570        .named_children(&mut cursor)
21571        .collect::<Vec<_>>();
21572    same_node(body_node, body)
21573        && constraint.kind() == "qualified_identifier"
21574        && constraint_scope.kind() == "namespace_identifier"
21575        && constraint_template.kind() == "template_type"
21576        && matches!(constraint_types.as_slice(), [left, right]
21577            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21578        && !constraint_arguments.has_error()
21579        && close_error.kind() == "ERROR"
21580        && close_error.named_child_count() == 0
21581        && storage.kind() == "storage_class_specifier"
21582        && return_type.kind() == "identifier"
21583        && node_text(return_type, source) == node_text(parameter_name, source)
21584        && extract_function_declarator(declarator)
21585            .and_then(cpp_function_declarator_name_node)
21586            .is_some()
21587}
21588
21589fn cpp_reparsed_template_macro_constructor_declarator<'tree>(
21590    node: Node<'tree>,
21591    parameter_name: Node<'_>,
21592    source: &str,
21593) -> Option<Node<'tree>> {
21594    let body = cpp_reparsed_member_function_body(node)?;
21595    let constraint = node.child_by_field_name("type")?;
21596    let constraint_template = constraint.child_by_field_name("name")?;
21597    let constraint_arguments = constraint_template.child_by_field_name("arguments")?;
21598    let mut argument_cursor = constraint_arguments.walk();
21599    let constraint_types = constraint_arguments
21600        .named_children(&mut argument_cursor)
21601        .collect::<Vec<_>>();
21602    if constraint.kind() != "qualified_identifier"
21603        || constraint_template.kind() != "template_type"
21604        || !matches!(constraint_types.as_slice(), [left, right]
21605            if left.kind() == "type_descriptor" && right.kind() == "type_descriptor")
21606        || constraint_arguments.has_error()
21607        || node
21608            .child_by_field_name("body")
21609            .is_none_or(|candidate| !same_node(candidate, body))
21610    {
21611        return None;
21612    }
21613
21614    let mut cursor = node.walk();
21615    let recovery_errors = node
21616        .named_children(&mut cursor)
21617        .filter(|child| child.kind() == "ERROR")
21618        .collect::<Vec<_>>();
21619    if !recovery_errors
21620        .iter()
21621        .any(|error| cpp_reparsed_constraint_macro_error(*error, source))
21622        || !recovery_errors.iter().all(|error| {
21623            error.named_child_count() == 0
21624                || cpp_reparsed_constraint_macro_error(*error, source)
21625                || (error.named_child_count() == 1
21626                    && error
21627                        .named_child(0)
21628                        .is_some_and(|child| child.kind() == "function_declarator"))
21629        })
21630    {
21631        return None;
21632    }
21633
21634    let parameter_text = node_text(parameter_name, source);
21635    let mut declarators = node
21636        .child_by_field_name("declarator")
21637        .and_then(extract_function_declarator)
21638        .into_iter()
21639        .collect::<Vec<_>>();
21640    for error in recovery_errors {
21641        let mut stack = vec![error];
21642        while let Some(current) = stack.pop() {
21643            if current.kind() == "function_declarator" {
21644                declarators.push(current);
21645            }
21646            let mut cursor = current.walk();
21647            stack.extend(current.named_children(&mut cursor));
21648        }
21649    }
21650    declarators.into_iter().find(|declarator| {
21651        cpp_function_declarator_name_node(*declarator)
21652            .is_some_and(|name| name.kind() == "identifier")
21653            && declarator
21654                .child_by_field_name("parameters")
21655                .is_some_and(|parameters| {
21656                    parameters
21657                        .named_children(&mut parameters.walk())
21658                        .filter_map(|parameter| parameter.child_by_field_name("type"))
21659                        .any(|parameter_type| node_text(parameter_type, source) == parameter_text)
21660                })
21661    })
21662}
21663
21664fn cpp_reparsed_template_macro_constructor_companion_is_indexable(
21665    node: Node<'_>,
21666    parameter_name: Node<'_>,
21667    source: &str,
21668) -> bool {
21669    cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source).is_some()
21670}
21671
21672fn cpp_reparsed_template_macro_function_companion_is_indexable(
21673    node: Node<'_>,
21674    parameter_name: Node<'_>,
21675    source: &str,
21676) -> bool {
21677    if node.has_error() || cpp_reparsed_member_function_body(node).is_none() {
21678        return false;
21679    }
21680    let Some(return_type) = node.child_by_field_name("type") else {
21681        return false;
21682    };
21683    let Some(function_declarator) = node
21684        .child_by_field_name("declarator")
21685        .and_then(extract_function_declarator)
21686    else {
21687        return false;
21688    };
21689    if cpp_function_declarator_name_node(function_declarator).is_none()
21690        || !cpp_reparsed_member_return_type_is_indexable(return_type, source)
21691    {
21692        return false;
21693    }
21694    let Some(parameters) = function_declarator.child_by_field_name("parameters") else {
21695        return false;
21696    };
21697    let parameter_text = node_text(parameter_name, source);
21698    parameters
21699        .named_children(&mut parameters.walk())
21700        .any(|parameter| {
21701            parameter
21702                .child_by_field_name("type")
21703                .is_some_and(|parameter_type| node_text(parameter_type, source) == parameter_text)
21704        })
21705}
21706
21707fn cpp_reparsed_constraint_macro_error(node: Node<'_>, source: &str) -> bool {
21708    if node.kind() != "ERROR" {
21709        return false;
21710    }
21711    let mut stack = vec![node];
21712    while let Some(current) = stack.pop() {
21713        let macro_shape = match current.kind() {
21714            "call_expression" => current
21715                .child_by_field_name("function")
21716                .zip(current.child_by_field_name("arguments")),
21717            "init_declarator" => current
21718                .child_by_field_name("declarator")
21719                .zip(current.child_by_field_name("value")),
21720            _ => None,
21721        };
21722        if let Some((name, arguments)) = macro_shape
21723            && name.kind() == "identifier"
21724            && arguments.kind() == "argument_list"
21725            && arguments.named_child_count() >= 2
21726            && cpp_export_macro_token(&normalize_cpp_whitespace(node_text(name, source)))
21727        {
21728            return true;
21729        }
21730        let mut cursor = current.walk();
21731        stack.extend(current.named_children(&mut cursor));
21732    }
21733    false
21734}
21735
21736fn cpp_recovered_template_macro_constructor<'tree>(
21737    node: Node<'tree>,
21738    source: &str,
21739) -> Option<(Node<'tree>, Node<'tree>)> {
21740    let mut prefix = node.prev_named_sibling()?;
21741    while prefix.kind() == "comment" {
21742        prefix = prefix.prev_named_sibling()?;
21743    }
21744    let parameter_name = cpp_reparsed_template_macro_prefix_parameter(prefix, source)?;
21745    let parameter = parameter_name
21746        .parent()
21747        .filter(|parent| parent.kind() == "type_parameter_declaration")?;
21748    let declarator =
21749        cpp_reparsed_template_macro_constructor_declarator(node, parameter_name, source)?;
21750    Some((declarator, parameter))
21751}
21752
21753/// The template parameter a split constraint-macro head declares for the
21754/// member function after it, when the recovery gate admits the pair.
21755/// `template <class U, REQUIRES("...", std::is_convertible<U, int>)>` leaves
21756/// the head as an `ERROR` beside a clean function definition; the parameter is
21757/// the head's structured `type_parameter_declaration`.
21758fn cpp_recovered_template_macro_function_parameter<'tree>(
21759    node: Node<'tree>,
21760    source: &str,
21761) -> Option<Node<'tree>> {
21762    let prefix = cpp_prev_non_comment_named_sibling(node)?;
21763    let parameter_name = cpp_reparsed_template_macro_constraint_prefix_parameter(prefix, source)?;
21764    if !cpp_reparsed_template_macro_function_companion_is_indexable(node, parameter_name, source) {
21765        return None;
21766    }
21767    parameter_name
21768        .parent()
21769        .filter(|parameter| parameter.kind() == "type_parameter_declaration")
21770}
21771
21772fn cpp_reparsed_template_macro_prefix_is_indexable(node: Node<'_>, source: &str) -> bool {
21773    if let Some(parameter_name) = cpp_reparsed_template_macro_prefix_parameter(node, source) {
21774        return cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21775            cpp_reparsed_template_macro_companion_is_indexable(function, parameter_name, source)
21776                || cpp_reparsed_template_macro_constructor_companion_is_indexable(
21777                    function,
21778                    parameter_name,
21779                    source,
21780                )
21781        });
21782    }
21783    let Some(parameter_name) =
21784        cpp_reparsed_template_macro_constraint_prefix_parameter(node, source)
21785    else {
21786        return false;
21787    };
21788    cpp_next_non_comment_named_sibling(node).is_some_and(|function| {
21789        cpp_reparsed_template_macro_function_companion_is_indexable(
21790            function,
21791            parameter_name,
21792            source,
21793        )
21794    })
21795}
21796
21797fn cpp_reparsed_member_function_is_indexable(node: Node<'_>, source: &str) -> bool {
21798    let function_name = node
21799        .child_by_field_name("declarator")
21800        .and_then(extract_function_declarator)
21801        .and_then(cpp_function_declarator_name_node);
21802    if let Some(body) = cpp_reparsed_member_function_body(node)
21803        && function_name.is_some()
21804        && cpp_reparsed_member_function_errors_are_in_body(node, body, source)
21805    {
21806        return true;
21807    }
21808    cpp_reparsed_attribute_member_function(node, source)
21809        || cpp_reparsed_friend_function_is_indexable(node, source)
21810        || cpp_reparsed_prefix_attribute_function_is_indexable(node, source)
21811        || cpp_reparsed_access_template_function_is_indexable(node, source)
21812        || cpp_recovered_template_macro_constructor(node, source).is_some()
21813}
21814
21815/// Recognize the three top-level nodes produced when an unknown attribute
21816/// macro separates an inline member's declarator from its body in a reparsed
21817/// class interior: an errorful declaration with a missing semicolon, the macro
21818/// call expression, and the complete compound body. Their adjacency and exact
21819/// structured shapes prove one recoverable member envelope; arbitrary calls or
21820/// blocks do not pass this gate.
21821fn cpp_reparsed_macro_attribute_member_sequence(
21822    children: &[Node<'_>],
21823    index: usize,
21824    source: &str,
21825) -> bool {
21826    let Some(prefix) = children.get(index).copied() else {
21827        return false;
21828    };
21829    let declaration = if prefix.kind() == "labeled_statement" {
21830        prefix
21831            .named_child(prefix.named_child_count().saturating_sub(1))
21832            .filter(|child| child.kind() == "declaration")
21833    } else {
21834        (prefix.kind() == "declaration").then_some(prefix)
21835    };
21836    let Some(declaration) = declaration else {
21837        return false;
21838    };
21839    if !declaration.has_error()
21840        || declaration
21841            .child_by_field_name("declarator")
21842            .and_then(extract_function_declarator)
21843            .and_then(cpp_function_declarator_name_node)
21844            .is_none()
21845    {
21846        return false;
21847    }
21848    let Some(attribute_statement) = children.get(index + 1).copied() else {
21849        return false;
21850    };
21851    let Some(attribute_call) = (attribute_statement.kind() == "expression_statement")
21852        .then(|| attribute_statement.named_child(0))
21853        .flatten()
21854        .filter(|child| child.kind() == "call_expression")
21855    else {
21856        return false;
21857    };
21858    let Some(attribute_name) = attribute_call
21859        .child_by_field_name("function")
21860        .filter(|function| function.kind() == "identifier")
21861        .map(|function| normalize_cpp_whitespace(node_text(function, source)))
21862    else {
21863        return false;
21864    };
21865    if !cpp_export_macro_token(&attribute_name) {
21866        return false;
21867    }
21868    let Some(body) = children.get(index + 2).copied() else {
21869        return false;
21870    };
21871    body.kind() == "compound_statement"
21872        && body.child(0).is_some_and(|open| open.kind() == "{")
21873        && body
21874            .child(body.child_count().saturating_sub(1))
21875            .is_some_and(|close| close.kind() == "}" && !close.is_missing())
21876        && declaration.end_byte() <= attribute_statement.start_byte()
21877        && attribute_statement.end_byte() <= body.start_byte()
21878}
21879
21880/// Whether a reparsed `ERROR` holds nothing but member declarations: a run of
21881/// types and specifiers followed by a declarator, over and over, with nothing
21882/// left over. A string-argument attribute macro is what strands them there, and
21883/// the walk indexes exactly what this reads, so admitting the region is safe
21884/// (#2552). Without it Botan's `DL_Group` was indexed with no members at all,
21885/// because one `BOTAN_DEPRECATED("...") explicit DL_Group(...)` member rejected
21886/// the whole class body.
21887fn cpp_reparsed_stranded_member_error(node: Node<'_>, source: &str) -> bool {
21888    if node.kind() != "ERROR" {
21889        return false;
21890    }
21891    let run = stranded_declaration_run(node, source);
21892    run.complete && !run.declarations.is_empty()
21893}
21894
21895/// The ABI 15 grammar reads a defaulted zero-argument constructor in a
21896/// translation-unit-shaped class-body reparse as an assignment expression:
21897/// `Ctor() = default`. Admit only the exact assignment shape, and only when
21898/// the same reparse retains a separate constructor declarator for that name.
21899/// The second condition keeps an arbitrary expression statement from becoming
21900/// a member merely because it contains a callee followed by `default`.
21901/// A constructor declaration a statement-context class-body reparse reads as
21902/// a bare call statement (`value(pn_data_t* d);`). At statement scope a
21903/// constructor declaration is indistinguishable from a call, but a direct
21904/// child of the reparse root whose callee names the class can only be the
21905/// constructor: a member with no return type is nothing else.
21906fn cpp_reparsed_bare_constructor_call<'tree>(
21907    node: Node<'tree>,
21908    class_name: &str,
21909    source: &str,
21910) -> Option<Node<'tree>> {
21911    if node.kind() != "expression_statement" {
21912        return None;
21913    }
21914    let call = node
21915        .named_child(0)
21916        .filter(|child| child.kind() == "call_expression")?;
21917    let function = call.child_by_field_name("function")?;
21918    (function.kind() == "identifier" && node_text(function, source).trim() == class_name)
21919        .then_some(call)
21920}
21921
21922fn cpp_reparsed_defaulted_constructor_is_indexable(
21923    node: Node<'_>,
21924    members: &[Node<'_>],
21925    class_name: Option<&str>,
21926    source: &str,
21927) -> bool {
21928    let Some(class_name) = class_name else {
21929        return false;
21930    };
21931    if node.kind() != "expression_statement" || node.named_child_count() != 1 {
21932        return false;
21933    }
21934    let Some(assignment) = node.named_child(0) else {
21935        return false;
21936    };
21937    if assignment.kind() != "assignment_expression" || assignment.child_count() != 3 {
21938        return false;
21939    }
21940    if assignment
21941        .child(1)
21942        .is_none_or(|operator| operator.kind() != "=")
21943    {
21944        return false;
21945    }
21946    let Some(call) = assignment.child_by_field_name("left") else {
21947        return false;
21948    };
21949    let Some(default_value) = assignment.child_by_field_name("right") else {
21950        return false;
21951    };
21952    let Some(function) = call.child_by_field_name("function") else {
21953        return false;
21954    };
21955    let Some(arguments) = call.child_by_field_name("arguments") else {
21956        return false;
21957    };
21958    if call.kind() != "call_expression"
21959        || function.kind() != "identifier"
21960        || arguments.kind() != "argument_list"
21961        || arguments.named_child_count() != 0
21962        || default_value.kind() != "identifier"
21963        || node_text(default_value, source) != "default"
21964        || node_text(function, source) != class_name
21965    {
21966        return false;
21967    }
21968    members.iter().any(|member| {
21969        if same_node(*member, node) {
21970            return false;
21971        }
21972        cpp_reparsed_member_constructor_name(*member, source).is_some_and(|name| name == class_name)
21973    })
21974}
21975
21976/// Return the constructor name from one direct class-member-shaped reparse
21977/// item. The function-declarator in an `ERROR` is admitted only through the
21978/// existing ERROR-only member proof; clean function definitions must carry no
21979/// return type and must retain either a body or ABI 15's `default_method_clause`.
21980fn cpp_reparsed_member_constructor_name(node: Node<'_>, source: &str) -> Option<String> {
21981    let function_declarator = match node.kind() {
21982        "function_definition"
21983            if !node.has_error() && node.child_by_field_name("type").is_none() =>
21984        {
21985            let has_body = node
21986                .child_by_field_name("body")
21987                .is_some_and(|body| body.kind() == "compound_statement");
21988            let has_default_method_clause = node
21989                .named_children(&mut node.walk())
21990                .any(|child| child.kind() == "default_method_clause");
21991            if !has_body && !has_default_method_clause {
21992                return None;
21993            }
21994            node.child_by_field_name("declarator")
21995                .and_then(extract_function_declarator)
21996        }
21997        "ERROR" if cpp_reparsed_member_error_is_indexable(node) => {
21998            let mut stack = node.named_children(&mut node.walk()).collect::<Vec<_>>();
21999            let mut candidate = None;
22000            while let Some(current) = stack.pop() {
22001                if current.kind() == "function_declarator" {
22002                    if candidate.is_some() {
22003                        return None;
22004                    }
22005                    candidate = Some(current);
22006                    continue;
22007                }
22008                if current.kind() == "ERROR" {
22009                    let mut cursor = current.walk();
22010                    stack.extend(current.named_children(&mut cursor));
22011                }
22012            }
22013            candidate
22014        }
22015        _ => None,
22016    }?;
22017    cpp_function_declarator_name_node(function_declarator)
22018        .map(|name| normalize_cpp_whitespace(node_text(name, source)))
22019}
22020
22021fn cpp_reparsed_members_are_indexable(root: Node<'_>, source: &str) -> bool {
22022    cpp_reparsed_members_are_indexable_for_class(root, source, None)
22023}
22024
22025fn cpp_reparsed_members_are_indexable_for_class(
22026    root: Node<'_>,
22027    source: &str,
22028    class_name: Option<&str>,
22029) -> bool {
22030    let mut saw_member = false;
22031    let mut regions = Vec::new();
22032    let mut region_tree: Option<Tree> = None;
22033    loop {
22034        let root = region_tree.as_ref().map_or(root, Tree::root_node);
22035        let children = root.named_children(&mut root.walk()).collect::<Vec<_>>();
22036        let mut index = 0;
22037        while index < children.len() {
22038            let child = children[index];
22039            if cpp_reparsed_macro_attribute_member_sequence(&children, index, source) {
22040                saw_member = true;
22041                index += 3;
22042                continue;
22043            }
22044            if let Some(recovered) = fragmented_class_body(child, source) {
22045                let Some(tree) = cpp_reparse_fragmented_class_body(
22046                    source,
22047                    recovered.body.reparse_start,
22048                    recovered.body.reparse_end,
22049                ) else {
22050                    return false;
22051                };
22052                if !cpp_reparsed_members_are_indexable_for_class(
22053                    tree.root_node(),
22054                    source,
22055                    Some(recovered.name.as_str()),
22056                ) {
22057                    return false;
22058                }
22059                saw_member = true;
22060                index += 1;
22061                while index < children.len()
22062                    && children[index].end_byte() <= recovered.body.class_range.end_byte
22063                {
22064                    index += 1;
22065                }
22066                continue;
22067            }
22068            match child.kind() {
22069                "comment" => {}
22070                "labeled_statement" => {
22071                    let Some(label) = child.child_by_field_name("label") else {
22072                        return false;
22073                    };
22074                    if !matches!(node_text(label, source), "public" | "protected" | "private") {
22075                        return false;
22076                    }
22077                    let payload = child
22078                        .named_children(&mut child.walk())
22079                        .filter(|node| node.id() != label.id() && node.kind() != "comment")
22080                        .collect::<Vec<_>>();
22081                    if payload.is_empty() {
22082                        return false;
22083                    }
22084                    // The statement-context grammar splits these constructors
22085                    // across an ERROR and initializer-looking declarations.
22086                    if class_name.is_some_and(|name| {
22087                        cpp_reparsed_synthetic_initializer_constructor(
22088                            child,
22089                            name,
22090                            source,
22091                            child.end_byte(),
22092                        )
22093                        .is_some()
22094                            || cpp_reparsed_preprocessor_constructor(child, name, source).is_some()
22095                    }) {
22096                        saw_member = true;
22097                        index += 1;
22098                        continue;
22099                    }
22100                    // A destructor in statement context is a unary '~' call,
22101                    // with a possible stranded virtual keyword ahead of it.
22102                    let destructor = match payload.as_slice() {
22103                        [expression] => Some(*expression),
22104                        [specifier, expression]
22105                            if specifier.kind() == "ERROR"
22106                                && specifier.child_count() == 1
22107                                && specifier
22108                                    .child(0)
22109                                    .is_some_and(|keyword| keyword.kind() == "virtual") =>
22110                        {
22111                            Some(*expression)
22112                        }
22113                        _ => None,
22114                    };
22115                    if destructor.is_some_and(|expression| {
22116                        expression.kind() == "expression_statement"
22117                            && !expression.has_error()
22118                            && expression.named_child(0).is_some_and(|unary| {
22119                                unary.kind() == "unary_expression"
22120                                    && unary
22121                                        .child_by_field_name("operator")
22122                                        .is_some_and(|operator| operator.kind() == "~")
22123                                    && unary.child_by_field_name("argument").is_some_and(|call| {
22124                                        call.kind() == "call_expression"
22125                                            && call.child_by_field_name("function").is_some_and(
22126                                                |name| {
22127                                                    name.kind() == "identifier"
22128                                                        && class_name.is_none_or(|owner| {
22129                                                            node_text(name, source) == owner
22130                                                        })
22131                                                },
22132                                            )
22133                                            && call.child_by_field_name("arguments").is_some_and(
22134                                                |arguments| arguments.named_child_count() == 0,
22135                                            )
22136                                    })
22137                            })
22138                    }) {
22139                        saw_member = true;
22140                    } else {
22141                        // A label puts its payload in statement context, where
22142                        // static assertions look like calls and alias templates
22143                        // can become ERROR nodes. Reparse after the colon as
22144                        // declarations and apply this same gate to that region.
22145                        let colon = child
22146                            .children(&mut child.walk())
22147                            .find(|node| node.kind() == ":")
22148                            .expect("an access label has a colon");
22149                        regions.push(colon.end_byte()..child.end_byte());
22150                    }
22151                }
22152                "function_definition" => {
22153                    if child.has_error()
22154                        && !cpp_reparsed_member_function_is_indexable(child, source)
22155                        && cpp_sentinel_macro_region(child, source).is_none()
22156                    {
22157                        return false;
22158                    }
22159                    saw_member = true;
22160                }
22161                // The attribute a string-argument macro leaves as a call statement
22162                // of its own. It declares nothing; the member it decorated is the
22163                // sibling after it, checked on its own turn.
22164                "expression_statement" | "ERROR" if is_string_attribute_macro_statement(child) => {}
22165                "expression_statement"
22166                    if cpp_reparsed_defaulted_constructor_is_indexable(
22167                        child, &children, class_name, source,
22168                    ) =>
22169                {
22170                    saw_member = true;
22171                }
22172                "expression_statement"
22173                    if class_name.is_some_and(|class_name| {
22174                        cpp_reparsed_bare_constructor_call(child, class_name, source).is_some()
22175                    }) =>
22176                {
22177                    saw_member = true;
22178                }
22179                "ERROR"
22180                    if (cpp_reparsed_member_error_is_indexable(child)
22181                        || cpp_reparsed_adjacent_copy_control_error(child, source)
22182                        || cpp_reparsed_stranded_member_error(child, source))
22183                        && (child
22184                        .next_named_sibling()
22185                        .is_some_and(|sibling| cpp_is_stray_semicolon(sibling, source))
22186                        || cpp_reparsed_member_error_with_preprocessed_body(child)
22187                        // The macro-declaration grammar folds the member after
22188                        // the copy-control pair into the same ERROR and leaves
22189                        // its body as the following sibling.
22190                        || (cpp_reparsed_adjacent_copy_control_error(child, source)
22191                            && child
22192                                .next_named_sibling()
22193                                .is_some_and(|sibling| sibling.kind() == "compound_statement"))) =>
22194                {
22195                    saw_member = true;
22196                }
22197                "ERROR" if cpp_reparsed_attribute_requires_error(child, source) => {
22198                    saw_member = true;
22199                }
22200                "ERROR" if cpp_reparsed_template_macro_prefix_is_indexable(child, source) => {
22201                    saw_member = true;
22202                }
22203                "expression_statement"
22204                    if cpp_is_stray_semicolon(child, source)
22205                        && child.prev_named_sibling().is_some_and(|error| {
22206                            cpp_reparsed_member_error_is_indexable(error)
22207                                || cpp_reparsed_adjacent_copy_control_error(error, source)
22208                                || cpp_reparsed_stranded_member_error(error, source)
22209                        }) =>
22210                {
22211                    saw_member = true;
22212                }
22213                "compound_statement"
22214                    if cpp_reparsed_constructor_body_is_indexable(child, source)
22215                        || cpp_reparsed_attribute_requires_body(child, source)
22216                        || child.prev_named_sibling().is_some_and(|error| {
22217                            cpp_reparsed_adjacent_copy_control_error(error, source)
22218                        }) =>
22219                {
22220                    saw_member = true;
22221                }
22222                // A copy-control declaration the macro-declaration grammar reads
22223                // as a function-like macro wrapping a type: `XMLElement( const
22224                // XMLElement& )` in a statement-context reparse is a
22225                // `macro_type_specifier`, admitted only where the parse would
22226                // otherwise contain an error. The live `;` beside it is the
22227                // declaration's own terminator, the same proof the ERROR
22228                // envelope's stray semicolon carried.
22229                "macro_type_specifier"
22230                    if child
22231                        .child_by_field_name("name")
22232                        .is_some_and(|name| name.kind() == "identifier")
22233                        && child
22234                            .child_by_field_name("type")
22235                            .is_some_and(|type_node| type_node.kind() == "type_descriptor")
22236                        && child.next_sibling().is_some_and(|sibling| {
22237                            sibling.kind() == ";" && !sibling.is_missing()
22238                        }) =>
22239                {
22240                    saw_member = true;
22241                }
22242                kind if cpp_is_indexable_item_kind(kind) => saw_member = true,
22243                _ => return false,
22244            }
22245            index += 1;
22246        }
22247        let Some(region) = regions.pop() else {
22248            return saw_member;
22249        };
22250        let Some(tree) = cpp_reparse_region_items(source, region.start, region.end) else {
22251            return false;
22252        };
22253        region_tree = Some(tree);
22254    }
22255}
22256
22257/// Detect the malformed constructor shape that tree-sitter exposes as an
22258/// access-label statement followed by initializer-looking declarations. The
22259/// declarations are not class members: visiting their `location(loc)` and
22260/// `string(s)` function declarators would publish synthetic functions. The
22261/// export-class fallback keeps the original sibling nodes and therefore avoids
22262/// this parser artifact. The returned range identifies the real constructor
22263/// header, which can be reparsed independently as a structured declarator.
22264fn cpp_reparsed_synthetic_initializer_constructor_range(
22265    root: Node<'_>,
22266    class_name: &str,
22267    source: &str,
22268    constructor_end: usize,
22269) -> Option<std::ops::Range<usize>> {
22270    let mut stack = {
22271        let mut cursor = root.walk();
22272        root.named_children(&mut cursor).collect::<Vec<_>>()
22273    };
22274    while let Some(current) = stack.pop() {
22275        if let Some(range) = cpp_reparsed_synthetic_initializer_constructor(
22276            current,
22277            class_name,
22278            source,
22279            constructor_end,
22280        ) {
22281            return Some(range);
22282        }
22283        if current.kind() == "ERROR" {
22284            let mut cursor = current.walk();
22285            stack.extend(current.named_children(&mut cursor));
22286        }
22287    }
22288    None
22289}
22290
22291/// Recover an inline constructor that a function-like export macro makes
22292/// tree-sitter merge with the following overload. In the reparsed class-body
22293/// region, the access label wraps one declaration whose ERROR contains the
22294/// constructor declarator and its base-initializer/body, while the declaration's
22295/// ordinary declarator is the following overload. Every boundary below comes
22296/// from that CST; no source syntax is reparsed by hand.
22297fn cpp_reparsed_merged_inline_constructor<'tree>(
22298    root: Node<'tree>,
22299    class_name: &str,
22300    source: &str,
22301) -> Option<(std::ops::Range<usize>, Node<'tree>)> {
22302    let mut stack = vec![root];
22303    while let Some(current) = stack.pop() {
22304        if current.kind() != "labeled_statement" {
22305            let mut cursor = current.walk();
22306            stack.extend(current.named_children(&mut cursor));
22307            continue;
22308        }
22309        let Some(declaration) = current
22310            .named_children(&mut current.walk())
22311            .find(|child| child.kind() == "declaration")
22312        else {
22313            continue;
22314        };
22315        if declaration
22316            .child_by_field_name("type")
22317            .is_none_or(|kind| node_text(kind, source).trim() != "explicit")
22318        {
22319            continue;
22320        }
22321        let following = declaration
22322            .child_by_field_name("declarator")
22323            .and_then(extract_function_declarator)
22324            .and_then(cpp_function_declarator_name_node);
22325        if following.is_none_or(|name| node_text(name, source).trim() != class_name) {
22326            continue;
22327        }
22328        let mut declaration_cursor = declaration.walk();
22329        let Some(error) = declaration
22330            .named_children(&mut declaration_cursor)
22331            .find(|child| child.kind() == "ERROR")
22332        else {
22333            continue;
22334        };
22335        let mut error_cursor = error.walk();
22336        let error_children = error.named_children(&mut error_cursor).collect::<Vec<_>>();
22337        let Some(constructor) = error_children.iter().copied().find(|child| {
22338            child.kind() == "function_declarator"
22339                && cpp_function_declarator_name_node(*child)
22340                    .is_some_and(|name| node_text(name, source).trim() == class_name)
22341        }) else {
22342            continue;
22343        };
22344        let Some(body) = error_children.iter().copied().find_map(|child| {
22345            (child.kind() == "init_declarator")
22346                .then(|| child.child_by_field_name("value"))
22347                .flatten()
22348                .filter(|value| value.kind() == "initializer_list")
22349        }) else {
22350            continue;
22351        };
22352        if constructor.end_byte() > body.start_byte() {
22353            continue;
22354        }
22355        return Some((constructor.start_byte()..body.end_byte(), body));
22356    }
22357    None
22358}
22359
22360fn cpp_reparsed_synthetic_initializer_constructor(
22361    node: Node<'_>,
22362    class_name: &str,
22363    source: &str,
22364    constructor_end: usize,
22365) -> Option<std::ops::Range<usize>> {
22366    if node.kind() != "labeled_statement" {
22367        return None;
22368    }
22369    let mut cursor = node.walk();
22370    let named = node
22371        .named_children(&mut cursor)
22372        .filter(|child| child.kind() != "comment")
22373        .collect::<Vec<_>>();
22374    let label = named.first()?;
22375    if label.kind() != "statement_identifier"
22376        || !matches!(
22377            node_text(*label, source).trim(),
22378            "public" | "private" | "protected"
22379        )
22380    {
22381        return None;
22382    }
22383    let call_error_index = named.iter().position(|child| {
22384        if child.kind() != "ERROR" {
22385            return false;
22386        }
22387        // The macro-declaration grammar keeps the constructor head's type as
22388        // the ERROR's own type field; the older grammar flattened the head
22389        // into a call expression inside it. ERROR nodes retain field labels
22390        // only by child index in runtime 0.25.
22391        let type_child = (0..child.child_count()).find_map(|index| {
22392            let candidate = child.child(index)?;
22393            (child.field_name_for_child(index as u32) == Some("type")
22394                && candidate.kind() == "type_identifier"
22395                && node_text(candidate, source).trim() == class_name)
22396                .then_some(candidate)
22397        });
22398        if type_child.is_some() {
22399            return true;
22400        }
22401        let mut stack = vec![*child];
22402        while let Some(current) = stack.pop() {
22403            if current.kind() == "call_expression"
22404                && current
22405                    .child_by_field_name("function")
22406                    .is_some_and(|function| {
22407                        function.kind() == "identifier"
22408                            && node_text(function, source).trim() == class_name
22409                    })
22410            {
22411                return true;
22412            }
22413            let mut cursor = current.walk();
22414            stack.extend(current.named_children(&mut cursor));
22415        }
22416        false
22417    })?;
22418    let constructor_start = {
22419        let head = named[call_error_index];
22420        let type_child = (0..head.child_count()).find_map(|index| {
22421            let candidate = head.child(index)?;
22422            (head.field_name_for_child(index as u32) == Some("type")
22423                && candidate.kind() == "type_identifier"
22424                && node_text(candidate, source).trim() == class_name)
22425                .then_some(candidate)
22426        });
22427        if let Some(type_node) = type_child {
22428            type_node.start_byte()
22429        } else {
22430            let mut stack = vec![head];
22431            let mut found = None;
22432            while let Some(current) = stack.pop() {
22433                if current.kind() == "call_expression"
22434                    && current
22435                        .child_by_field_name("function")
22436                        .is_some_and(|function| {
22437                            function.kind() == "identifier"
22438                                && node_text(function, source).trim() == class_name
22439                        })
22440                {
22441                    found = Some(current);
22442                    break;
22443                }
22444                let mut cursor = current.walk();
22445                stack.extend(current.named_children(&mut cursor));
22446            }
22447            found?.start_byte()
22448        }
22449    };
22450    named.iter().skip(call_error_index + 1).find(|child| {
22451        child.kind() == "declaration" && child.has_error() && {
22452            let mut cursor = child.walk();
22453            child.named_children(&mut cursor).any(|declarator| {
22454                declarator.kind() == "init_declarator"
22455                    && declarator
22456                        .child_by_field_name("declarator")
22457                        .is_some_and(|declarator| declarator.kind() == "function_declarator")
22458                    && declarator
22459                        .child_by_field_name("value")
22460                        .is_some_and(|value| value.kind() == "initializer_list")
22461            })
22462        }
22463    })?;
22464    Some(constructor_start..constructor_end)
22465}
22466
22467fn cpp_reparsed_exact_constructor_declarator<'tree>(
22468    root: Node<'tree>,
22469    start: usize,
22470    class_name: &str,
22471    source: &str,
22472) -> Option<Node<'tree>> {
22473    let mut candidate = None;
22474    let mut stack = vec![root];
22475    while let Some(current) = stack.pop() {
22476        if current.kind() == "function_declarator"
22477            && current.start_byte() == start
22478            && cpp_function_declarator_name_node(current)
22479                .is_some_and(|name| node_text(name, source).trim() == class_name)
22480        {
22481            if candidate.is_some() {
22482                return None;
22483            }
22484            candidate = Some(current);
22485            continue;
22486        }
22487        let mut cursor = current.walk();
22488        stack.extend(current.named_children(&mut cursor));
22489    }
22490    candidate
22491}
22492
22493fn cpp_is_indexable_item_kind(kind: &str) -> bool {
22494    matches!(
22495        kind,
22496        "namespace_definition"
22497            | "class_specifier"
22498            | "struct_specifier"
22499            | "union_specifier"
22500            | "enum_specifier"
22501            | "function_definition"
22502            | "template_declaration"
22503            | "declaration"
22504            | "field_declaration"
22505            | "alias_declaration"
22506            | "static_assert_declaration"
22507            | "type_definition"
22508            | "using_declaration"
22509            | "linkage_specification"
22510            | "preproc_def"
22511            | "preproc_function_def"
22512            | "preproc_include"
22513            | "preproc_if"
22514            | "preproc_ifdef"
22515            | "preproc_call"
22516    )
22517}
22518
22519fn cpp_overload_discriminator(declarator: Node<'_>, source: &str) -> Option<[u8; 32]> {
22520    use brokk_bifrost_core::analyzer::tree_walk::{
22521        canonical_type_tokens, overload_discriminator, overload_parameter_tokens,
22522    };
22523    let mut tokens = Vec::new();
22524    let parameters = declarator.child_by_field_name("parameters")?;
22525    for slot in cpp_callable_parameter_slots(parameters, source).ok()? {
22526        match slot {
22527            CppParameterSlot::Declared {
22528                parameter, name, ..
22529            } => {
22530                // The `(^name)` of a Blocks formal sits inside the recovered
22531                // type; the written name never distinguishes an overload.
22532                match cpp_block_pointer_formal(parameter) {
22533                    Some(block) => {
22534                        tokens.extend(overload_parameter_tokens(Some(block.result), source));
22535                        tokens.push("<block-pointer>".to_owned());
22536                    }
22537                    None => tokens.extend(overload_parameter_tokens(
22538                        parameter.child_by_field_name("type"),
22539                        source,
22540                    )),
22541                }
22542                let mut cursor = parameter.walk();
22543                for qualifier in parameter.named_children(&mut cursor) {
22544                    if qualifier.kind() == "type_qualifier" {
22545                        tokens.extend(canonical_type_tokens(qualifier, source));
22546                    }
22547                }
22548                // Pointer/reference/array/function declarators carry type
22549                // structure outside the base type. Exclude only the name.
22550                if let Some(declarator) = parameter.child_by_field_name("declarator") {
22551                    let mut pending = vec![(declarator, name)];
22552                    while let Some((node, excluded_name)) = pending.pop() {
22553                        if Some(node) == excluded_name {
22554                            continue;
22555                        }
22556                        if node.child_count() == 0 {
22557                            tokens.extend(canonical_type_tokens(node, source));
22558                        } else {
22559                            let parameter = matches!(
22560                                node.kind(),
22561                                "parameter_declaration" | "optional_parameter_declaration"
22562                            );
22563                            let excluded_name = if parameter {
22564                                node.child_by_field_name("declarator")
22565                                    .and_then(crate::structural::declarator_name_node)
22566                            } else {
22567                                excluded_name
22568                            };
22569                            let default = parameter
22570                                .then(|| node.child_by_field_name("default_value"))
22571                                .flatten();
22572                            for index in (0..node.child_count()).rev() {
22573                                let child = node.child(index).expect("child");
22574                                if Some(child) != default {
22575                                    pending.push((child, excluded_name));
22576                                }
22577                            }
22578                        }
22579                    }
22580                }
22581            }
22582            CppParameterSlot::Ellipsis => tokens.push("<ellipsis>".to_owned()),
22583        }
22584    }
22585    let mut cursor = declarator.walk();
22586    let mut qualifiers = declarator
22587        .named_children(&mut cursor)
22588        .filter(|node| node.kind() == "type_qualifier")
22589        .map(|node| canonical_type_tokens(node, source))
22590        .collect::<Vec<_>>();
22591    qualifiers.sort();
22592    for qualifier in qualifiers {
22593        tokens.push("<member-qualifier>".to_owned());
22594        tokens.extend(qualifier);
22595    }
22596    for qualifier in declarator.named_children(&mut cursor) {
22597        if qualifier.kind() == "ref_qualifier" {
22598            tokens.push("<member-qualifier>".to_owned());
22599            tokens.extend(canonical_type_tokens(qualifier, source));
22600        }
22601    }
22602    Some(overload_discriminator(tokens))
22603}
22604
22605fn cpp_synthetic_name(node: Node<'_>) -> brokk_bifrost_core::analyzer::tree_walk::SyntheticName {
22606    brokk_bifrost_core::analyzer::tree_walk::synthetic_name(
22607        node,
22608        |candidate| match candidate.kind() {
22609            "compound_statement" => Some("block"),
22610            "struct_specifier" | "union_specifier" | "class_specifier"
22611                if candidate.child_by_field_name("name").is_none() =>
22612            {
22613                Some("anonymous")
22614            }
22615            _ => None,
22616        },
22617        |candidate| {
22618            candidate.kind() == "function_definition"
22619                || matches!(
22620                    candidate.kind(),
22621                    "class_specifier"
22622                        | "struct_specifier"
22623                        | "union_specifier"
22624                        | "namespace_definition"
22625                ) && candidate.child_by_field_name("name").is_some()
22626        },
22627    )
22628}
22629
22630#[cfg(test)]
22631mod tests {
22632
22633    #[test]
22634    fn recovered_callable_grammar_and_original_bytes() {
22635        use crate::grammar_test_support::{SOURCE, at, field, nodes, tree};
22636
22637        let parsed = tree(SOURCE);
22638        // These are the current grammar's contracts, including the macro_modifier
22639        // support that has replaced some older ERROR/argument_list recoveries.
22640        // Each row selects a real source region and independently pins its fields
22641        // and the tokens that must survive a synthetic class-context reparse.
22642        let cases = [
22643            (
22644                "expression_statement",
22645                58..72,
22646                &[][..],
22647                &[(58..64, "Widget"), (65..70, "value")][..],
22648            ),
22649            (
22650                "field_declaration",
22651                92..145,
22652                &[
22653                    ("type", "qualified_identifier", 111..122),
22654                    ("declarator", "function_declarator", 123..144),
22655                ][..],
22656                &[
22657                    (123..131, "toString"),
22658                    (132..137, "Yield"),
22659                    (138..143, "yield"),
22660                ][..],
22661            ),
22662            (
22663                "function_definition",
22664                147..191,
22665                &[
22666                    ("type", "qualified_identifier", 151..161),
22667                    ("declarator", "parenthesized_declarator", 161..174),
22668                    ("body", "compound_statement", 189..191),
22669                ][..],
22670                &[
22671                    (155..161, "Widget"),
22672                    (162..167, "Value"),
22673                    (168..173, "value"),
22674                ][..],
22675            ),
22676            (
22677                "field_declaration",
22678                193..296,
22679                &[
22680                    ("type", "macro_type_specifier", 193..222),
22681                    ("declarator", "macro_modifier_declarator", 223..263),
22682                    ("default_value", "initializer_list", 291..293),
22683                ][..],
22684                &[
22685                    (233..239, "Widget"),
22686                    (240..248, "grow_fun"),
22687                    (249..253, "grow"),
22688                ][..],
22689            ),
22690            (
22691                "call_expression",
22692                58..71,
22693                &[
22694                    ("function", "identifier", 58..64),
22695                    ("arguments", "argument_list", 64..71),
22696                ][..],
22697                &[][..],
22698            ),
22699            (
22700                "function_declarator",
22701                123..144,
22702                &[
22703                    ("declarator", "field_identifier", 123..131),
22704                    ("parameters", "parameter_list", 131..144),
22705                ][..],
22706                &[][..],
22707            ),
22708            (
22709                "function_declarator",
22710                233..263,
22711                &[
22712                    ("declarator", "field_identifier", 233..239),
22713                    ("parameters", "parameter_list", 239..254),
22714                ][..],
22715                &[][..],
22716            ),
22717        ];
22718        for (kind, span, fields, tokens) in cases {
22719            let node = at(parsed.root_node(), kind, span.clone());
22720            for (name, child_kind, child_span) in fields {
22721                field(node, name, child_kind, child_span.clone());
22722            }
22723            if tokens.is_empty() {
22724                continue;
22725            }
22726            // Match visit_bare_constructor_expression's documented class context.
22727            // The initializer-list body bounds the initialized-constructor region;
22728            // its recovery field also swallowed the original class close.
22729            let end = node
22730                .child_by_field_name("default_value")
22731                .map_or(span.end, |body| body.end_byte());
22732            const PREFIX: &str = "struct __bifrost_recovered_constructor { ";
22733            let parsed = crate::graph::syntax::parse_sentinel(
22734                PREFIX,
22735                crate::graph::syntax::SentinelInput::region(&SOURCE[span.start..end]),
22736                " };",
22737            );
22738            let (synthetic, reparsed) = (parsed.source, parsed.tree);
22739            for (original_span, text) in tokens {
22740                assert_eq!(&SOURCE[original_span.clone()], *text);
22741                let translated = PREFIX.len() + original_span.start - span.start
22742                    ..PREFIX.len() + original_span.end - span.start;
22743                let leaves: Vec<_> = nodes(reparsed.root_node())
22744                    .into_iter()
22745                    .filter(|token| token.child_count() == 0 && token.byte_range() == translated)
22746                    .collect();
22747                assert_eq!(leaves.len(), 1, "reparsed token {text} at {translated:?}");
22748                let token = leaves[0];
22749                assert_eq!(token.utf8_text(synthetic.as_bytes()).unwrap(), *text);
22750                assert_eq!(
22751                    token.start_byte() - PREFIX.len() + span.start
22752                        ..token.end_byte() - PREFIX.len() + span.start,
22753                    original_span.clone()
22754                );
22755                assert!(SOURCE.is_char_boundary(original_span.start));
22756                assert!(SOURCE.is_char_boundary(original_span.end));
22757            }
22758        }
22759    }
22760
22761    use super::*;
22762    use crate::adapter::parse_cpp_file;
22763    use brokk_bifrost_core::analyzer::parsed_file::{
22764        finish_declaration_identity_comparison_probe, start_declaration_identity_comparison_probe,
22765    };
22766    use brokk_bifrost_test_macros::hardwon;
22767    use std::fmt::Write;
22768
22769    #[test]
22770    fn synthetic_declaration_ids_follow_rank_across_edits() {
22771        fn ids(source: &str) -> std::collections::BTreeSet<String> {
22772            let parsed = parse_cpp_declarations(source, "rank.cpp");
22773            parsed
22774                .declarations()
22775                .iter()
22776                .filter(|unit| unit.identifier() == "Target")
22777                .map(|unit| unit.declaration_id().to_string())
22778                .collect()
22779        }
22780        let source = "void f(){ { class Target {}; } }";
22781        let original = ids(source);
22782        assert!(!original.is_empty(), "target declarations were produced");
22783        assert_eq!(original, ids(&format!("// byte shift\n{source}")));
22784        let inserted = ids("void f(){ {} { class Target {}; } }");
22785        assert_eq!(original.len(), inserted.len());
22786        assert!(
22787            original.is_disjoint(&inserted),
22788            "earlier siblings change ranks"
22789        );
22790    }
22791
22792    #[test]
22793    fn overload_synthetic_declarations_keep_distinct_ids() {
22794        let parsed = parse_cpp_declarations(
22795            "struct Owner { void f(int) { { class Marker {}; } } void f(double) { { class Marker {}; } } };",
22796            "overloads.cpp",
22797        );
22798        let ids: Vec<_> = parsed
22799            .declarations()
22800            .iter()
22801            .filter(|unit| unit.identifier() == "Marker")
22802            .map(|unit| unit.declaration_id())
22803            .collect();
22804        assert_eq!(ids.len(), 2, "both overloads keep their local declaration");
22805        assert_ne!(
22806            ids[0], ids[1],
22807            "local declarations have distinct identities"
22808        );
22809    }
22810
22811    fn parse_cpp_declarations(source: &str, name: &str) -> ParsedFile {
22812        let tree = crate::diagnostics::parse_cpp_tree(source);
22813        let file = ProjectFile::new(std::env::temp_dir(), name);
22814        parse_cpp_file(&file, source, &tree)
22815    }
22816
22817    #[test]
22818    #[hardwon(
22819        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: comment-only enums invented fields"
22820    )]
22821    fn comment_only_enums_have_no_enumerators() {
22822        let parsed =
22823            parse_cpp_declarations("enum Empty { /* no entries, still empty */ };", "empty.hpp");
22824        let names: Vec<_> = parsed
22825            .declarations()
22826            .iter()
22827            .map(|unit| unit.fq_name())
22828            .collect();
22829        assert_eq!(names, ["Empty"]);
22830    }
22831
22832    #[test]
22833    #[hardwon(
22834        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: GNU must-use attribute order lost obligations"
22835    )]
22836    fn quicksilver_gnu_attribute_orders() {
22837        for attributes in [
22838            "deprecated, warn_unused_result",
22839            "warn_unused_result, deprecated",
22840        ] {
22841            let source = format!("int value() __attribute__(({attributes}));");
22842            let tree = crate::diagnostics::parse_cpp_tree(&source);
22843            assert!(
22844                !tree.root_node().has_error(),
22845                "{}",
22846                tree.root_node().to_sexp()
22847            );
22848            let declaration = tree.root_node().named_child(0).unwrap();
22849            assert!(
22850                cpp_declaration_has_native_must_use_attribute(declaration, &source),
22851                "{source}: {}",
22852                declaration.to_sexp()
22853            );
22854        }
22855    }
22856
22857    #[test]
22858    #[hardwon(
22859        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: raw strings disabled fragmented brace matching"
22860    )]
22861    fn fragmented_brace_matching_ignores_literal_tokens() {
22862        // Isolate the brace proof from export-head recognition: the damaged
22863        // field list supplies the same container boundary used by recovery.
22864        let source =
22865            r#"struct Widget { @; const char* text = R"tag(} /* { ")tag"; void after(); };"#;
22866        let tree = crate::diagnostics::parse_cpp_tree(source);
22867        let tokens = crate::grammar_test_support::nodes(tree.root_node());
22868        assert!(
22869            tokens
22870                .iter()
22871                .any(|node| node.kind() == "raw_string_literal")
22872        );
22873        let body = tokens
22874            .iter()
22875            .find(|node| node.kind() == "field_declaration_list")
22876            .unwrap();
22877        let opening = body.child(0).unwrap();
22878        let head = body.named_child(0).unwrap();
22879        assert_eq!(opening.kind(), "{");
22880        assert!(body.has_error());
22881        assert_eq!(
22882            fragmented_container_close_class_body(head, opening, source)
22883                .map(|body| body.reparse_end),
22884            source.rfind('}')
22885        );
22886    }
22887
22888    #[test]
22889    #[hardwon(
22890        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: uppercase class names became attribute names"
22891    )]
22892    fn quicksilver_uppercase_attributed_classes() {
22893        for (name, body) in [
22894            ("Box", "{ int value; }"),
22895            ("BOX", "{ int value; }"),
22896            ("API", ""),
22897        ] {
22898            let source = format!("class [[deprecated]] {name} {body};");
22899            let parsed = parse_cpp_declarations(&source, "attributed-class.hpp");
22900            let names = parsed
22901                .declarations()
22902                .iter()
22903                .filter(|unit| unit.is_class())
22904                .map(|unit| unit.identifier().to_owned())
22905                .collect::<Vec<_>>();
22906            assert_eq!(names, [name], "{source}");
22907        }
22908    }
22909
22910    #[test]
22911    #[hardwon(
22912        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: positional export recovery rejected uppercase class names"
22913    )]
22914    fn quicksilver_fragmented_export_uppercase_classes() {
22915        for name in ["Box", "BOX"] {
22916            let source = r#"
22917namespace api {
22918
22919/**
22920* Doc comment
22921*/
22922class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
22923   public:
22924      /**
22925      * Construct from a point.
22926      */
22927      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
22928
22929#if defined(PROJECT_HAS_LEGACY_POINT)
22930      /**
22931      * Construct from a legacy point.
22932      */
22933      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
22934#endif
22935
22936      std::string algo_name() const override;
22937
22938      AlgorithmIdentifier algorithm_identifier() const override;
22939};
22940
22941}
22942"#
22943            .replace("VirtualBased", name);
22944            // Positional recovery once named this class. Since
22945            // brokk-tree-sitter-cpp 0.24.7 the grammar reads the head, and an
22946            // uppercase name must survive that reading too.
22947            let tree = crate::grammar_test_support::tree(&source);
22948            crate::grammar_test_support::assert_function_like_export_class_head(
22949                &tree, &source, name,
22950            );
22951            let parsed = parse_cpp_declarations(&source, "virtual-base.hpp");
22952            let explicit = parse_cpp_declarations(
22953                &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
22954                "virtual-base.hpp",
22955            );
22956            assert_eq!(
22957                declaration_expansion_inventory(&parsed),
22958                declaration_expansion_inventory(&explicit),
22959                "{name}"
22960            );
22961            let declarations = parsed.declarations();
22962            let class = declarations
22963                .iter()
22964                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
22965                .unwrap_or_else(|| panic!("{name}: missing class: {declarations:#?}"));
22966            assert_eq!(
22967                parsed.raw_supertypes.get(class),
22968                Some(&vec!["BaseKey".to_string()]),
22969                "{name}"
22970            );
22971            assert!(
22972                declarations.iter().any(|unit| {
22973                    unit.is_function() && unit.fq_name() == format!("api.{name}.algo_name")
22974                }),
22975                "{name}: the class must own its members: {declarations:#?}"
22976            );
22977        }
22978    }
22979
22980    #[test]
22981    #[hardwon(
22982        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: unrelated labels aborted constructor search"
22983    )]
22984    fn quicksilver_constructor_search_skips_other_labels() {
22985        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";
22986        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
22987        let (range, body) =
22988            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
22989                .unwrap_or_else(|| panic!("{}", tree.root_node().to_sexp()));
22990        assert_eq!(
22991            &source[range],
22992            "Lookup_Error(std::string_view err) : Exception(err) {}"
22993        );
22994        assert_eq!(node_text(body, source), "{}");
22995    }
22996
22997    #[test]
22998    #[hardwon(
22999        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovery admitted expression-only access labels"
23000    )]
23001    fn quicksilver_member_gate_checks_label_payload() {
23002        for (source, expected) in [
23003            ("public: int value;", true),
23004            (r#"public: static_assert(sizeof(int) > 0, "size");"#, true),
23005            (
23006                "private: template <class K> using key_arg = typename KeyArgImpl::template type<K, key_type>;",
23007                true,
23008            ),
23009            ("public: do_work();", false),
23010            ("ordinary: int value;", false),
23011        ] {
23012            let tree = cpp_reparse_fragmented_class_body(source, 0, source.len()).unwrap();
23013            assert_eq!(
23014                cpp_reparsed_members_are_indexable(tree.root_node(), source),
23015                expected,
23016                "{source}: {}",
23017                tree.root_node().to_sexp()
23018            );
23019        }
23020    }
23021
23022    #[test]
23023    #[hardwon(
23024        "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: sentinel recovery rejected uppercase classes"
23025    )]
23026    fn quicksilver_sentinel_uppercase_classes() {
23027        for name in ["Box", "BOX"] {
23028            let source = format!(
23029                "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"
23030            );
23031            let tree = crate::diagnostics::parse_cpp_tree(&source);
23032            let recovered = cpp_sentinel_recovered_classes(tree.root_node(), &source);
23033            assert!(
23034                recovered
23035                    .iter()
23036                    .any(|class| class.scope_components == ["absl", "container_internal", name]),
23037                "{source}: {recovered:#?}"
23038            );
23039        }
23040    }
23041
23042    #[test]
23043    fn native_must_use_attribute_nodes_are_structural() {
23044        let source = "int must_use_value() __attribute__((warn_unused_result));\nint must_use_value() { return 1; }\n[[nodiscard]] int standard_value();\n";
23045        let tree = crate::diagnostics::parse_cpp_tree(source);
23046        let root = tree.root_node();
23047        let mut declarations = Vec::new();
23048        walk_named_tree_preorder(root, true, |node| {
23049            if matches!(
23050                node.kind(),
23051                "declaration" | "function_definition" | "field_declaration"
23052            ) {
23053                declarations.push(node);
23054            }
23055            WalkControl::Continue
23056        });
23057        assert_eq!(
23058            declarations
23059                .into_iter()
23060                .map(|declaration| {
23061                    cpp_declaration_has_native_must_use_attribute(declaration, source)
23062                })
23063                .collect::<Vec<_>>(),
23064            vec![true, false, true]
23065        );
23066        let enum_source = "[[nodiscard]] enum Marker { MarkerValue } marker;";
23067        let enum_tree = crate::diagnostics::parse_cpp_tree(enum_source);
23068        let mut enum_specifier = None;
23069        walk_named_tree_preorder(enum_tree.root_node(), true, |node| {
23070            if node.kind() == "enum_specifier" {
23071                enum_specifier = Some(node);
23072                WalkControl::Break
23073            } else {
23074                WalkControl::Continue
23075            }
23076        });
23077        assert!(cpp_declaration_has_native_must_use_attribute(
23078            enum_specifier.expect("enum declaration"),
23079            enum_source
23080        ));
23081    }
23082
23083    fn recorded_callable_modifiers(parsed: &ParsedFile) -> Vec<String> {
23084        let mut rows = parsed
23085            .signature_metadata
23086            .iter()
23087            .flat_map(|(unit, entries)| {
23088                entries.iter().map(move |metadata| {
23089                    assert!(
23090                        metadata.callable_modifiers_recorded(),
23091                        "{} must record that the walk read its declaration shape",
23092                        unit.fq_name()
23093                    );
23094                    format!(
23095                        "{} decl_only={} static={} constructor={} visibility={:?}",
23096                        unit.fq_name(),
23097                        metadata.is_declaration_only(),
23098                        metadata.callable_is_static(),
23099                        metadata.callable_is_constructor(),
23100                        metadata
23101                            .callable_declared_visibility()
23102                            .expect("a recorded modifier set states a visibility"),
23103                    )
23104                })
23105            })
23106            .collect::<Vec<_>>();
23107        rows.sort();
23108        rows
23109    }
23110
23111    /// #3520: the input-edge rebuild of a rendered C++ name must render back to
23112    /// exactly the spelling it was given, with the nested-class `$` recorded as
23113    /// a [`SegmentKind::Nested`] join and a member as its own tail segment.
23114    #[test]
23115    #[hardwon("#3520: exact C++ lookup could not round-trip nested terminal identities")]
23116    fn rendered_name_fq_round_trips_nested_class_spellings() {
23117        assert!(cpp_rendered_name_fq("").is_empty());
23118        for rendered in [
23119            "Only",
23120            "Top$Only",
23121            "ns.Outer$Inner",
23122            "Top$Only.b",
23123            "ns.Outer$Inner.a",
23124            "Buck$Stop",
23125        ] {
23126            let fq = cpp_rendered_name_fq(rendered);
23127            assert_eq!(
23128                fq.display_native(Language::Cpp, segment_interner()),
23129                rendered,
23130                "native round trip for {rendered}"
23131            );
23132        }
23133        let kinds: Vec<SegmentKind> = cpp_rendered_name_fq("ns.Outer$Inner")
23134            .segments()
23135            .iter()
23136            .map(|segment| segment_interner().resolve(*segment).1)
23137            .collect();
23138        assert_eq!(
23139            kinds,
23140            [
23141                SegmentKind::Unknown,
23142                SegmentKind::Unknown,
23143                SegmentKind::Nested
23144            ]
23145        );
23146    }
23147
23148    /// #3631: a `::`-only source spelling must name the nested `$` identity
23149    /// and the namespace `.` identity. Canonical `.` / `$` spellings stay on
23150    /// the single rendered-name parse.
23151    #[test]
23152    #[hardwon(
23153        "509d66c85788: C++ declaration recovery regressed for colon source aliases name nested and namespace identities"
23154    )]
23155    fn colon_source_aliases_name_nested_and_namespace_identities() {
23156        let native = |rendered: &str| {
23157            cpp_colon_source_aliases(rendered)
23158                .into_iter()
23159                .map(|fq| fq.display_native(Language::Cpp, segment_interner()))
23160                .collect::<Vec<_>>()
23161        };
23162        let top = native("Top::Only");
23163        assert!(top.contains(&"Top$Only".to_string()), "{top:?}");
23164        assert!(top.contains(&"Top.Only".to_string()), "{top:?}");
23165        assert!(top.contains(&"Top::Only".to_string()), "{top:?}");
23166        let nested = native("ns::Outer::Inner");
23167        assert!(nested.contains(&"ns.Outer$Inner".to_string()), "{nested:?}");
23168        assert!(
23169            nested.contains(&"ns::Outer.Inner".to_string()),
23170            "{nested:?}"
23171        );
23172        let member = native("Top::Only::b");
23173        assert!(member.contains(&"Top$Only.b".to_string()), "{member:?}");
23174        assert!(cpp_colon_source_aliases("Top$Only").is_empty());
23175        assert!(cpp_colon_source_aliases("ns.Outer$Inner").is_empty());
23176        assert!(cpp_colon_source_aliases("base::android.ScopedJavaGlobalRef").is_empty());
23177        assert!(cpp_colon_source_aliases("Only").is_empty());
23178        assert!(cpp_colon_source_aliases("Top::Only").iter().all(|fq| {
23179            fq.segments()
23180                .iter()
23181                .all(|segment| segment_interner().resolve(*segment).1 != SegmentKind::Unknown)
23182        }));
23183    }
23184
23185    #[test]
23186    #[hardwon(
23187        "aa581047b253: C++ declaration recovery regressed for displaced terminator ignores nested initializer endif"
23188    )]
23189    fn displaced_terminator_ignores_nested_initializer_endif() {
23190        let source = "#ifdef ENABLE_ITEMS\n\
23191struct Item { int reg; };\n\
23192static const struct Item items[] = {{1}};\n\
23193static const struct Item extras[] = {\n\
23194  {0},\n\
23195#ifndef REDUCED\n\
23196  {1},\n\
23197#endif\n\
23198};\n\
23199int read_item(int i) { return items[i].reg; }\n\
23200#endif\n";
23201        let mut parser = tree_sitter::Parser::new();
23202        parser
23203            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23204            .expect("C++ grammar");
23205        let tree = parser.parse(source, None).expect("fixture tree");
23206        let conditional = tree
23207            .root_node()
23208            .named_child(0)
23209            .filter(|node| node.kind() == "preproc_ifdef")
23210            .expect("outer conditional");
23211
23212        assert!(conditional.has_error());
23213        assert!(cpp_displaced_preprocessor_terminator(conditional).is_none());
23214        assert!(cpp_displaced_preprocessor_boundary(conditional).is_none());
23215    }
23216
23217    #[test]
23218    #[hardwon(
23219        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23220    )]
23221    fn pyobject_head_field_recovery_publishes_only_the_real_member() {
23222        let source = "struct Image { PyObject_HEAD Imaging image; };";
23223        let parsed = parse_cpp_declarations(source, "image.h");
23224        let names = parsed
23225            .declarations()
23226            .iter()
23227            .map(|unit| unit.fq_name())
23228            .collect::<Vec<_>>();
23229
23230        assert!(names.iter().any(|name| name == "Image.image"), "{names:#?}");
23231        assert!(
23232            names.iter().all(|name| name != "Image.Imaging"),
23233            "the pseudo-declarator must not become a field: {names:#?}"
23234        );
23235
23236        let pointer = parse_cpp_declarations(
23237            "struct Image { PyObject_HEAD Imaging *image; };",
23238            "image-pointer.h",
23239        );
23240        let pointer_names = pointer
23241            .declarations()
23242            .iter()
23243            .map(|unit| unit.fq_name())
23244            .collect::<Vec<_>>();
23245        assert!(
23246            pointer_names.iter().any(|name| name == "Image.image"),
23247            "the pointer-shaped declaration keeps its ordinary declarator path: {pointer_names:#?}"
23248        );
23249        assert!(
23250            pointer_names.iter().all(|name| name != "Image.Imaging"),
23251            "the pointer recovery error must not become a field: {pointer_names:#?}"
23252        );
23253
23254        let unrelated_macro = parse_cpp_declarations(
23255            "struct Image { OTHER_HEAD Imaging other; };",
23256            "image-near-miss.h",
23257        );
23258        let unrelated_names = unrelated_macro
23259            .declarations()
23260            .iter()
23261            .map(|unit| unit.fq_name())
23262            .collect::<Vec<_>>();
23263        assert!(
23264            unrelated_names.iter().any(|name| name == "Image.other"),
23265            "an object-like macro in specifier position is a `macro_modifier`, so the \
23266             member is the structured best-effort reading, the same one the \
23267             macro-declaration grammar gives `ffc_internal` and `PyObject_HEAD`: {unrelated_names:#?}"
23268        );
23269        assert!(
23270            unrelated_names
23271                .iter()
23272                .all(|name| name != "Image.OTHER_HEAD"),
23273            "the macro marker must not become a declaration of its own: {unrelated_names:#?}"
23274        );
23275    }
23276
23277    #[test]
23278    #[hardwon(
23279        "376bffa02aec: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23280    )]
23281    fn gtest_style_stolen_namespace_recovery_never_retains_class_owner() {
23282        let source = r#"namespace testing {
23283namespace internal {
23284    namespace detail {
23285        class GTEST_API_ [[nodiscard]] ScopedFakeTestPartResultReporter {
23286        public:
23287            int value() const { return count_ + 1; }
23288        private:
23289            int count_;
23290        };
23291        class GTEST_API_ [[nodiscard]] OtherReporter {
23292        public:
23293            int value() const { return count_ + 2; }
23294        private:
23295            int count_;
23296        };
23297    }
23298
23299    template <typename T>
23300    void CmpHelperSTRNE(ScopedFakeTestPartResultReporter<T> const& value);
23301
23302    class TailReporter {};
23303}
23304}
23305"#;
23306        let parsed = parse_cpp_declarations(source, "gtest-recovery.h");
23307        let declarations = parsed.declarations();
23308        let mut parser = tree_sitter::Parser::new();
23309        parser
23310            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23311            .unwrap();
23312        let tree = parser.parse(source, None).unwrap();
23313        let tail_start = source.find("TailReporter").expect("tail class");
23314        let tail_node = tree
23315            .root_node()
23316            .named_descendant_for_byte_range(tail_start, tail_start + "TailReporter".len())
23317            .expect("tail class AST node");
23318        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23319        assert!(
23320            tree.root_node().has_error(),
23321            "the malformed class must exercise recovery"
23322        );
23323        assert!(index.region_at(tail_start).is_some());
23324        assert_eq!(
23325            index.enclosing_namespace_components(tail_node, source),
23326            ["testing", "internal"]
23327        );
23328        let file = ProjectFile::new(std::env::temp_dir(), "gtest-recovery.h");
23329        let mut recovered_parsed = ParsedFile::new(String::new());
23330        let class_unit = CodeUnit::new_fq(
23331            file.clone(),
23332            CodeUnitType::Class,
23333            "testing",
23334            "ScopedFakeTestPartResultReporter",
23335            cpp_member_fq("testing", "ScopedFakeTestPartResultReporter"),
23336        );
23337        let scope = ScopeInfo {
23338            package_name: "testing".to_string(),
23339            module: None,
23340            class_unit: Some(class_unit),
23341            template_signature: Some("<typename T>".to_string()),
23342            template_metadata: Some(CppTemplateMetadata {
23343                primary_name: "ScopedFakeTestPartResultReporter".to_string(),
23344                primary_fq_name: String::new(),
23345                parameters: Vec::new(),
23346                specialization_arguments: Vec::new(),
23347                alias_target: None,
23348            }),
23349            declarations_are_fields: true,
23350            recovered_specialization_member_scope: true,
23351            visible_using_namespaces: Vec::new(),
23352            local_block: None,
23353            visible_using_declarations: Arc::default(),
23354        };
23355        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23356        let properties = RefCell::new(HashMap::default());
23357        let dependencies =
23358            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23359        let ancestry = ParentIndex::new(tree.root_node());
23360        let mut visitor = CppVisitor {
23361            file: &file,
23362            source,
23363            parsed: &mut recovered_parsed,
23364            source_collector: &collector,
23365            source_properties: &properties,
23366            source_dependencies: &dependencies,
23367            primary_ancestry: &ancestry,
23368            primary_root_id: tree.root_node().id(),
23369            callable_observer: &IgnoreCppCallableObservations,
23370            source_links: HashMap::default(),
23371            source_declarations: HashSet::default(),
23372            preprocessor_context_cache: Default::default(),
23373            active_source_context: None,
23374            c_tag_semantics: false,
23375            c_tag_scope_witness: false,
23376            recovered_class_sibling_scopes: HashMap::default(),
23377            consumed_fragment_regions: Vec::new(),
23378            orphaned_namespaces: &index,
23379            partitioned_regions: Vec::new(),
23380            namespace_forward_scans: HashMap::default(),
23381            recovery_captures: Vec::new(),
23382            object_macro_fields: HashMap::default(),
23383            ambiguous_object_macro_fields: HashSet::default(),
23384        };
23385        let recovered = visitor
23386            .recovered_namespace_scope(tail_node, &scope)
23387            .expect("the tail must use the stolen namespace scope");
23388        assert_eq!(recovered.package_name, "testing::internal");
23389        assert!(
23390            recovered.class_unit.is_none(),
23391            "recovered namespace declarations cannot retain the malformed class owner"
23392        );
23393        assert!(recovered.template_signature.is_none());
23394        assert!(recovered.template_metadata.is_none());
23395        assert!(!recovered.declarations_are_fields);
23396        assert!(!recovered.recovered_specialization_member_scope);
23397        assert!(
23398            declarations
23399                .iter()
23400                .any(|unit| unit.fq_name() == "testing::internal.TailReporter"),
23401            "the stolen namespace tail remains in its recovered namespace: {declarations:#?}"
23402        );
23403        assert!(
23404            declarations
23405                .iter()
23406                .any(|unit| unit.fq_name() == "testing::internal.CmpHelperSTRNE"),
23407            "the recovered free function remains in its namespace: {declarations:#?}"
23408        );
23409        assert!(
23410            declarations
23411                .iter()
23412                .any(|unit| { unit.fq_name() == "testing::internal::detail.OtherReporter.value" }),
23413            "the independent nested class keeps its ordinary class owner: {declarations:#?}"
23414        );
23415        assert!(
23416            declarations.iter().all(|unit| {
23417                !unit
23418                    .short_name()
23419                    .contains("ScopedFakeTestPartResultReporter.CmpHelperSTRNE")
23420            }),
23421            "recovered namespace declarations must not retain a class owner: {declarations:#?}"
23422        );
23423        assert!(
23424            declarations
23425                .iter()
23426                .all(|unit| !unit.identifier().is_empty()),
23427            "the minimized gtest recovery must never mint an empty FqName segment: {declarations:#?}"
23428        );
23429    }
23430
23431    /// #4292: a recovered namespace level whose close the brace stack could not
23432    /// prove keeps its head-only range, and the first declaration the walk
23433    /// reaches inside the region is not the namespace. The Module's name
23434    /// occurrence must be the level's own name token; deriving it from the
23435    /// walk's current node paired the head range with an unrelated name and
23436    /// panicked the collector's containment assertion.
23437    #[test]
23438    fn recovered_namespace_levels_name_their_own_name_tokens() {
23439        // #3084's collapsed-head fixture without the namespace's own close:
23440        // the file ends inside the namespace, so the brace stack proves no
23441        // close for its `{` and the level keeps its `namespace Name {` head
23442        // alone (#3328).
23443        let source = r#"namespace outer::app {
23444
23445    class Target {
23446        int value_;
23447    };
23448
23449    template <typename T>
23450    class Holder {
23451    public:
23452        explicit constexpr Holder( T lhs ): m_lhs( lhs ) {}
23453
23454#define HOLDER_DEFINE_OP( id, op )                                             \
23455    template <typename U>                                                      \
23456    constexpr friend auto operator op( Holder&& lhs, U&& rhs )                 \
23457        -> std::enable_if_t<is_##id##_comparable<T, U>::value, Target> {       \
23458        return Target{};                                                       \
23459    }
23460
23461        HOLDER_DEFINE_OP( equal, == )
23462#undef HOLDER_DEFINE_OP
23463        T m_lhs;
23464    };
23465
23466    class Tail {};
23467"#;
23468        let parsed = parse_cpp_declarations(source, "recovered-namespace-name.h");
23469        let mut parser = tree_sitter::Parser::new();
23470        parser
23471            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23472            .unwrap();
23473        let tree = parser.parse(source, None).unwrap();
23474        assert!(
23475            tree.root_node().has_error(),
23476            "the fixture must reproduce the parse damage"
23477        );
23478        let target_start = source.find("class Target").expect("target class");
23479        let target_node = tree
23480            .root_node()
23481            .named_descendant_for_byte_range(target_start, target_start + "class".len())
23482            .expect("target class AST node");
23483        let index = OrphanedNamespaceScopeIndex::build(tree.root_node(), source);
23484        // The macro-declaration grammar keeps the namespace head and its
23485        // members inside a surviving `namespace_definition`; the missing close
23486        // no longer collapses the head into an ERROR, so the scope authority
23487        // answers from parsed ancestry rather than a recovered region.
23488        assert_eq!(
23489            index.enclosing_namespace_components(target_node, source),
23490            ["outer", "app"]
23491        );
23492        let file = ProjectFile::new(std::env::temp_dir(), "recovered-namespace-name.h");
23493        let mut recovered_parsed = ParsedFile::new(String::new());
23494        let collector = RefCell::new(PrimarySourceFactCollector::new(source));
23495        let properties = RefCell::new(HashMap::default());
23496        let dependencies =
23497            RefCell::new(crate::source_dependencies::DeclarationDependencies::default());
23498        let ancestry = ParentIndex::new(tree.root_node());
23499        let mut visitor = CppVisitor {
23500            file: &file,
23501            source,
23502            parsed: &mut recovered_parsed,
23503            source_collector: &collector,
23504            source_properties: &properties,
23505            source_dependencies: &dependencies,
23506            primary_ancestry: &ancestry,
23507            primary_root_id: tree.root_node().id(),
23508            callable_observer: &IgnoreCppCallableObservations,
23509            source_links: HashMap::default(),
23510            source_declarations: HashSet::default(),
23511            preprocessor_context_cache: Default::default(),
23512            active_source_context: None,
23513            c_tag_semantics: false,
23514            c_tag_scope_witness: false,
23515            recovered_class_sibling_scopes: HashMap::default(),
23516            consumed_fragment_regions: Vec::new(),
23517            orphaned_namespaces: &index,
23518            partitioned_regions: Vec::new(),
23519            namespace_forward_scans: HashMap::default(),
23520            recovery_captures: Vec::new(),
23521            object_macro_fields: HashMap::default(),
23522            ambiguous_object_macro_fields: HashSet::default(),
23523        };
23524        // The surviving `namespace_definition` takes the ordinary namespace
23525        // visit, which declares each level with its own name token.
23526        visitor.visit_container(
23527            tree.root_node(),
23528            &ancestry,
23529            "",
23530            None,
23531            None,
23532            None,
23533            Vec::new(),
23534            Arc::default(),
23535        );
23536        let collector = collector.borrow();
23537        for (module_name, expected_name) in [
23538            (
23539                "outer",
23540                Range {
23541                    start_byte: 10,
23542                    end_byte: 15,
23543                    start_line: 1,
23544                    end_line: 1,
23545                },
23546            ),
23547            (
23548                "outer::app",
23549                Range {
23550                    start_byte: 17,
23551                    end_byte: 20,
23552                    start_line: 1,
23553                    end_line: 1,
23554                },
23555            ),
23556        ] {
23557            let declaration = visitor
23558                .source_links
23559                .iter()
23560                .find(|(unit, _)| unit.fq_name() == module_name)
23561                .expect("the recovered module links a source declaration")
23562                .1
23563                .last()
23564                .expect("the recovered module declaration");
23565            let declaration = collector.declaration(*declaration);
23566            let name = declaration
23567                .name
23568                .expect("the recovered module records its name occurrence");
23569            assert_eq!(
23570                collector.occurrence(name).range,
23571                expected_name,
23572                "the recovered module names its own token: {module_name}"
23573            );
23574        }
23575        assert!(
23576            parsed.declarations().iter().any(|unit| {
23577                unit.fq_name() == "outer::app.Target" || unit.fq_name() == "outer::app.Tail"
23578            }),
23579            "the recovered namespace members keep their namespace: {:#?}",
23580            parsed.declarations()
23581        );
23582    }
23583
23584    #[test]
23585    #[hardwon(
23586        "3d77aac19607: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23587    )]
23588    fn object_like_field_macros_materialize_owner_specific_declarations() {
23589        let source = r#"#define PUBLIC_FIELDS int public_value;
23590#define PRIVATE_FIELDS int private_value;
23591#define NOT_A_FIELD_LIST not a declaration
23592
23593struct First {
23594  PUBLIC_FIELDS
23595  PRIVATE_FIELDS
23596};
23597struct Second {
23598  PUBLIC_FIELDS
23599  NOT_A_FIELD_LIST
23600};
23601#undef PUBLIC_FIELDS
23602struct Third {
23603  PUBLIC_FIELDS
23604};
23605"#;
23606        let parsed = parse_cpp_declarations(source, "macro-fields.c");
23607        let fields = parsed
23608            .declarations()
23609            .iter()
23610            .filter(|unit| unit.is_field())
23611            .map(|unit| unit.fq_name())
23612            .collect::<Vec<_>>();
23613
23614        assert!(
23615            fields.contains(&"First.public_value".to_string()),
23616            "{fields:?}"
23617        );
23618        assert!(
23619            fields.contains(&"First.private_value".to_string()),
23620            "{fields:?}"
23621        );
23622        assert!(
23623            fields.contains(&"Second.public_value".to_string()),
23624            "{fields:?}"
23625        );
23626        assert!(
23627            !fields.iter().any(|field| field.contains("not_a_field")),
23628            "malformed macro must fail closed: {fields:?}"
23629        );
23630        assert!(
23631            !fields.iter().any(|field| field.starts_with("Third.")),
23632            "undefined macro must fail closed: {fields:?}"
23633        );
23634    }
23635
23636    #[test]
23637    #[hardwon(
23638        "daa20f600d11: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
23639    )]
23640    fn macro_redefinitions_keep_distinct_structured_declaration_identities() {
23641        let source = "#define VALUE 1\n#undef VALUE\n#define VALUE 2\n";
23642        let parsed = parse_cpp_declarations(source, "macro-redefinition.c");
23643        let mut macros = parsed
23644            .declarations()
23645            .iter()
23646            .filter(|unit| unit.is_macro() && unit.identifier() == "VALUE")
23647            .collect::<Vec<_>>();
23648        macros.sort_by_key(|unit| parsed.declaration_ranges(unit)[0].start_byte);
23649
23650        assert_eq!(macros.len(), 2, "{macros:#?}");
23651        assert_eq!(macros[0].signature(), Some("#define VALUE 1"));
23652        assert_eq!(macros[1].signature(), Some("#define VALUE 2"));
23653        assert_eq!(parsed.declaration_ranges(macros[0])[0].start_byte, 0);
23654        assert_eq!(
23655            parsed.declaration_ranges(macros[1])[0].start_byte,
23656            source.rfind("#define VALUE 2").expect("second definition")
23657        );
23658    }
23659
23660    #[test]
23661    #[hardwon(
23662        "5de822e5d66f: C++ declaration recovery regressed for identifies export macro class base displaced into declarator"
23663    )]
23664    fn identifies_export_macro_class_base_displaced_into_declarator() {
23665        let source = r#"#define PROJECT_API_
23666namespace project {
23667namespace internal {
23668template <typename T>
23669class Base {};
23670}
23671template <typename T>
23672class Wrapper;
23673template <>
23674class PROJECT_API_ [[nodiscard]] Wrapper<int> : public internal::Base<int> {};
23675}
23676"#;
23677        let mut parser = tree_sitter::Parser::new();
23678        parser
23679            .set_language(&tree_sitter_cpp::LANGUAGE.into())
23680            .unwrap();
23681        let tree = parser.parse(source, None).unwrap();
23682        let start = source.find("internal::Base<int>").expect("base");
23683        let mut base = tree
23684            .root_node()
23685            .descendant_for_byte_range(start, start + 8)
23686            .expect("base syntax");
23687        while base.kind() != "qualified_identifier" {
23688            base = base.parent().expect("qualified base ancestor");
23689        }
23690        assert!(
23691            is_recovered_exported_class_base_type_node(base, source),
23692            "{}",
23693            tree.root_node().to_sexp()
23694        );
23695    }
23696
23697    fn all_function_identities(parsed: &ParsedFile) -> Vec<(String, Option<String>)> {
23698        parsed
23699            .declarations()
23700            .iter()
23701            .filter(|unit| unit.is_function())
23702            .map(|unit| (unit.fq_name(), unit.signature().map(str::to_string)))
23703            .collect()
23704    }
23705
23706    fn function_identities(parsed: &ParsedFile) -> Vec<(String, String)> {
23707        let mut identities = all_function_identities(parsed)
23708            .into_iter()
23709            .map(|(name, signature)| (name, signature.unwrap_or_default()))
23710            .collect::<Vec<_>>();
23711        identities.sort();
23712        identities
23713    }
23714
23715    /// #2932. Recover the parser-owned declaration shapes for known pre-ANSI
23716    /// prototype macros. Each case is parsed independently so a preceding
23717    /// malformed line cannot flatten the next declaration's fields into one
23718    /// cascading `ERROR`; that shape has no structural proof and must fail
23719    /// closed rather than fall back to a token scan. Parameter names are
23720    /// dropped from signatures just as they are for ordinary C prototypes.
23721    #[test]
23722    #[hardwon(
23723        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers parser owned declaration shapes"
23724    )]
23725    fn c_prototype_macro_recovers_parser_owned_declaration_shapes() {
23726        let cases = [
23727            (
23728                "VALUE pg_typemap_fit_to_result _(( VALUE, VALUE ));",
23729                "pg_typemap_fit_to_result",
23730                "(VALUE, VALUE)",
23731            ),
23732            (
23733                "VALUE pg_typemap_result_value _(( t_typemap *, VALUE, int, int ));",
23734                "pg_typemap_result_value",
23735                "(t_typemap *, VALUE, int, int)",
23736            ),
23737            (
23738                "void pg_typemap_mark _(( void * ));",
23739                "pg_typemap_mark",
23740                "(void *)",
23741            ),
23742            (
23743                "void init_pg_type_map _(( void ));",
23744                "init_pg_type_map",
23745                "(void)",
23746            ),
23747            ("static VALUE pg_static _(( void ));", "pg_static", "(void)"),
23748            (
23749                "extern VALUE pg_extern _(( VALUE, VALUE ));",
23750                "pg_extern",
23751                "(VALUE, VALUE)",
23752            ),
23753            (
23754                "size_t pg_typemap_memsize _(( const void * ));",
23755                "pg_typemap_memsize",
23756                "(const void *)",
23757            ),
23758            (
23759                "VALUE pg_wrap_socket_io _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));",
23760                "pg_wrap_socket_io",
23761                "(int, VALUE, VALUE *, int *)",
23762            ),
23763            ("VALUE pg_dunder __P(( VALUE ));", "pg_dunder", "(VALUE)"),
23764            ("VALUE pg_of OF(( VALUE ));", "pg_of", "(VALUE)"),
23765            ("VALUE pg_proto PROTO(( VALUE ));", "pg_proto", "(VALUE)"),
23766        ];
23767
23768        for (index, (source, name, signature)) in cases.into_iter().enumerate() {
23769            let parsed = parse_cpp_declarations(source, &format!("prototype_{index}.h"));
23770            assert_eq!(
23771                function_identities(&parsed),
23772                vec![(name.to_string(), signature.to_string())],
23773                "{source}: {:#?}",
23774                parsed.declarations()
23775            );
23776            assert!(
23777                parsed.declarations().iter().all(|unit| !unit.is_field()),
23778                "{source} must not retain the malformed field: {:#?}",
23779                parsed.declarations()
23780            );
23781        }
23782    }
23783
23784    /// #2932. `T *name _(( args ));` with a pointer return type and a simple
23785    /// argument list parses with no `ERROR` node at all -- tree-sitter reads
23786    /// it as a multiplication of a type by a call
23787    /// (`identifier "T" '*' call_expression{qualified_identifier{...}}`),
23788    /// wrapped in an `expression_statement` that `has_error()` only because
23789    /// of the `MISSING "::"` inside the `qualified_identifier`. Reproduced
23790    /// here with a clean preceding field so the prototype remains the
23791    /// parser-owned `expression_statement` this recovery requires.
23792    #[test]
23793    #[hardwon(
23794        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers pointer return expression statements"
23795    )]
23796    fn c_prototype_macro_recovers_pointer_return_expression_statements() {
23797        let cases = [
23798            (
23799                "PGconn *pg_get_pgconn _(( VALUE ));",
23800                "pg_get_pgconn",
23801                "(VALUE)",
23802            ),
23803            (
23804                "PGresult* pgresult_get _(( VALUE ));",
23805                "pgresult_get",
23806                "(VALUE)",
23807            ),
23808        ];
23809        for (index, (prototype, name, signature)) in cases.into_iter().enumerate() {
23810            let source = format!("extern VALUE rb_mPG;\n{prototype}\n");
23811            let parsed = parse_cpp_declarations(&source, &format!("pointer_{index}.h"));
23812            assert_eq!(
23813                function_identities(&parsed),
23814                vec![(name.to_string(), signature.to_string())],
23815                "{source}: {:#?}",
23816                parsed.declarations()
23817            );
23818            assert_eq!(
23819                parsed
23820                    .declarations()
23821                    .iter()
23822                    .filter(|unit| unit.is_field())
23823                    .map(|unit| unit.fq_name())
23824                    .collect::<Vec<_>>(),
23825                vec!["rb_mPG".to_string()],
23826                "{source}: {:#?}",
23827                parsed.declarations()
23828            );
23829        }
23830    }
23831
23832    /// #6902. A class-body prototype macro line is a `field_declaration`. It
23833    /// takes the same candidate gate and reparse as a file-scope one, inside
23834    /// the class envelope, so the member keeps its owner, its written range,
23835    /// its `static`, and the access its label states.
23836    #[test]
23837    #[hardwon("#6902: a class-body __P prototype stayed a field outside prototype recovery")]
23838    fn c_prototype_macro_recovers_class_body_members() {
23839        const SOURCE: &str = concat!(
23840            "class Pg {\n",
23841            "  VALUE wrap _(( VALUE self ));\n",
23842            "public:\n",
23843            "  int invoke __P((int value));\n",
23844            "  static char *name PROTO(( int index ));\n",
23845            "};\n",
23846        );
23847        let parsed = parse_cpp_declarations(SOURCE, "pg.h");
23848        assert_eq!(
23849            function_identities(&parsed),
23850            vec![
23851                ("Pg.invoke".to_string(), "(int)".to_string()),
23852                ("Pg.name".to_string(), "(int)".to_string()),
23853                ("Pg.wrap".to_string(), "(VALUE)".to_string()),
23854            ],
23855            "{:#?}",
23856            parsed.declarations()
23857        );
23858        assert!(
23859            parsed.declarations().iter().all(|unit| !unit.is_field()),
23860            "the malformed fields must not survive: {:#?}",
23861            parsed.declarations()
23862        );
23863        let class = parsed
23864            .declarations()
23865            .iter()
23866            .find(|unit| unit.is_class() && unit.fq_name() == "Pg")
23867            .expect("class Pg");
23868        let mut children = parsed
23869            .children
23870            .get(class)
23871            .into_iter()
23872            .flatten()
23873            .map(|unit| unit.fq_name())
23874            .collect::<Vec<_>>();
23875        children.sort();
23876        assert_eq!(children, vec!["Pg.invoke", "Pg.name", "Pg.wrap"]);
23877        for (name, written) in [
23878            ("Pg.wrap", "VALUE wrap _(( VALUE self ));"),
23879            ("Pg.invoke", "int invoke __P((int value));"),
23880            ("Pg.name", "static char *name PROTO(( int index ));"),
23881        ] {
23882            let unit = parsed
23883                .declarations()
23884                .iter()
23885                .find(|unit| unit.is_function() && unit.fq_name() == name)
23886                .unwrap_or_else(|| panic!("missing recovered member {name}"));
23887            let [range] = parsed.declaration_ranges(unit) else {
23888                panic!("{name} must have exactly one range");
23889            };
23890            assert_eq!(&SOURCE[range.start_byte..range.end_byte], written, "{name}");
23891        }
23892        assert_eq!(
23893            recorded_callable_modifiers(&parsed),
23894            vec![
23895                "Pg.invoke decl_only=true static=false constructor=false visibility=Public"
23896                    .to_string(),
23897                "Pg.name decl_only=true static=true constructor=false visibility=Public"
23898                    .to_string(),
23899                "Pg.wrap decl_only=true static=false constructor=false visibility=Private"
23900                    .to_string(),
23901            ],
23902        );
23903    }
23904
23905    /// #2932 acceptance. Keep the exact issue witness inside the surrounding
23906    /// `ext/pg.h` block, where tree-sitter's cascading recovery produces both
23907    /// field-preserving declaration shapes and ambiguous flattened `ERROR`
23908    /// regions. The witnessed declaration must still become a Function and
23909    /// its type must not survive as the name of a bogus Field. This test does
23910    /// not claim that structurally flattened neighboring prototypes are safe
23911    /// to recover.
23912    #[test]
23913    #[hardwon(
23914        "398ac6605e9f: C++ declaration recovery regressed for c prototype macro recovers the issue witness inside the real ruby pg header block"
23915    )]
23916    fn c_prototype_macro_recovers_the_issue_witness_inside_the_real_ruby_pg_header_block() {
23917        let source = r#"VALUE pg_typemap_fit_to_result                         _(( VALUE, VALUE ));
23918VALUE pg_typemap_fit_to_query                          _(( VALUE, VALUE ));
23919int pg_typemap_fit_to_copy_get                         _(( VALUE ));
23920VALUE pg_typemap_result_value                          _(( t_typemap *, VALUE, int, int ));
23921t_pg_coder *pg_typemap_typecast_query_param            _(( t_typemap *, VALUE, int ));
23922VALUE pg_typemap_typecast_copy_get                     _(( t_typemap *, VALUE, int, int, int ));
23923void pg_typemap_mark                                   _(( void * ));
23924size_t pg_typemap_memsize                              _(( const void * ));
23925void pg_typemap_compact                                _(( void * ));
23926
23927PGconn *pg_get_pgconn                                  _(( VALUE ));
23928t_pg_connection *pg_get_connection                     _(( VALUE ));
23929VALUE pgconn_block                                     _(( int, VALUE *, VALUE ));
23930#ifdef __GNUC__
23931__attribute__((format(printf, 3, 4)))
23932#endif
23933NORETURN(void pg_raise_conn_error                      _(( VALUE klass, VALUE self, const char *format, ...)));
23934VALUE pg_wrap_socket_io                                _(( int sd, VALUE self, VALUE *p_socket_io, int *p_ruby_sd ));
23935void pg_unwrap_socket_io                               _(( VALUE self, VALUE *p_socket_io, int ruby_sd ));
23936
23937
23938VALUE pg_new_result                                    _(( PGresult *, VALUE ));
23939VALUE pg_new_result_autoclear                          _(( PGresult *, VALUE ));
23940PGresult* pgresult_get                                 _(( VALUE ));
23941VALUE pg_result_check                                  _(( VALUE ));
23942VALUE pg_result_clear                                  _(( VALUE ));
23943VALUE pg_tuple_new                                     _(( VALUE, int ));
23944
23945/*
23946 * Fetch the data pointer for the result object
23947 */
23948static inline t_pg_result *
23949pgresult_get_this( VALUE self )
23950{
23951	return RTYPEDDATA_DATA(self);
23952}
23953
23954
23955rb_encoding * pg_get_pg_encname_as_rb_encoding         _(( const char * ));
23956const char * pg_get_rb_encoding_as_pg_encoding         _(( rb_encoding * ));
23957rb_encoding *pg_conn_enc_get                           _(( PGconn * ));
23958
23959"#;
23960        let parsed = parse_cpp_declarations(source, "pg.h");
23961        assert!(
23962            function_identities(&parsed)
23963                .iter()
23964                .any(|(name, signature)| name == "pg_typemap_result_value"
23965                    && signature == "(t_typemap *, VALUE, int, int)"),
23966            "the real issue witness must be a Function with its C signature: {:#?}",
23967            parsed.declarations()
23968        );
23969        assert!(
23970            parsed
23971                .declarations()
23972                .iter()
23973                .all(|unit| !(unit.is_field() && unit.identifier() == "VALUE")),
23974            "the issue witness must not leave its return type as a Field name: {:#?}",
23975            parsed.declarations()
23976        );
23977    }
23978
23979    /// #2552 shape 1. Tree-sitter glues an attribute-like macro that stands
23980    /// between `explicit` and a constructor name onto the name, producing a
23981    /// `qualified_identifier` whose `::` it had to invent. The declared member
23982    /// is the constructor, not `MACRO Ctor`. The second constructor, with the
23983    /// macro in type position, was already recovered and is the control that
23984    /// both spellings agree.
23985    #[test]
23986    #[hardwon(
23987        "6b1e2f76b805: C++ declaration recovery regressed for a macro decorated constructor is named for the constructor"
23988    )]
23989    fn a_macro_decorated_constructor_is_named_for_the_constructor() {
23990        let source = r#"class SIMD_4x26 final {
23991   public:
23992      explicit BOTAN_FN_ISA_AVX2 SIMD_4x26(int v) : m_v(v) {}
23993      BOTAN_FN_ISA_AVX2 SIMD_4x26() : m_v(0) {}
23994      int m_v;
23995};
23996"#;
23997        let parsed = parse_cpp_declarations(source, "simd_4x26.h");
23998        assert_eq!(
23999            function_identities(&parsed),
24000            vec![
24001                ("SIMD_4x26.SIMD_4x26".to_string(), "()".to_string()),
24002                ("SIMD_4x26.SIMD_4x26".to_string(), "(int)".to_string()),
24003            ],
24004            "{:#?}",
24005            parsed.declarations()
24006        );
24007    }
24008
24009    #[test]
24010    #[hardwon(
24011        "e3ff69209ab2: C++ variable recovery must keep the terminal name after a missing scope separator"
24012    )]
24013    fn a_missing_scope_separator_keeps_variable_name_and_real_qualified_names() {
24014        let source = r#"struct cpu_spec {};
24015static struct cpu_spec __initdata cpu_specs[];
24016static struct cpu_spec ns::cpu_specs[];
24017"#;
24018        let tree = crate::diagnostics::parse_cpp_tree(source);
24019        let root = tree.root_node();
24020        let declarations = root
24021            .named_children(&mut root.walk())
24022            .filter(|node| node.kind() == "declaration")
24023            .collect::<Vec<_>>();
24024        let names = declarations
24025            .iter()
24026            .filter_map(|declaration| declaration.child_by_field_name("declarator"))
24027            .filter_map(|declarator| extract_variable_name(declarator, source))
24028            .collect::<Vec<_>>();
24029        assert_eq!(names, ["cpu_specs", "ns::cpu_specs"]);
24030    }
24031
24032    /// #2552 shape 2. `DEPRECATED(decl, "hint");` makes tree-sitter emit one
24033    /// `ERROR` holding the wrapped declaration and every declaration after it
24034    /// until the parser recovers. All of them must be indexed, each with the
24035    /// byte range that spells it.
24036    #[test]
24037    #[hardwon(
24038        "a0b3bd93484b: C++ declaration recovery regressed for a macro wrapped declaration and the declarations it swallowed are indexed"
24039    )]
24040    fn a_macro_wrapped_declaration_and_the_declarations_it_swallowed_are_indexed() {
24041        let source = r#"#include <cstdint>
24042struct llama_vocab; struct llama_model; struct llama_context; struct llama_context_params {};
24043    DEPRECATED(LLAMA_API struct llama_context * llama_new_context_with_model(
24044                     struct llama_model * model,
24045              struct llama_context_params   params),
24046            "use llama_init_from_model instead");
24047    LLAMA_API int32_t llama_tokenize(
24048        const struct llama_vocab * vocab,
24049                      const char * text,
24050                            bool   parse_special);
24051    LLAMA_API int32_t llama_other(int a);
24052"#;
24053        let parsed = parse_cpp_declarations(source, "llama.h");
24054        assert_eq!(
24055            function_identities(&parsed),
24056            vec![
24057                (
24058                    "llama_new_context_with_model".to_string(),
24059                    "(struct llama_model *, struct llama_context_params)".to_string()
24060                ),
24061                ("llama_other".to_string(), "(int)".to_string()),
24062                (
24063                    "llama_tokenize".to_string(),
24064                    "(const struct llama_vocab *, const char *, bool)".to_string()
24065                ),
24066            ],
24067            "{:#?}",
24068            parsed.declarations()
24069        );
24070
24071        // Each recovered declaration owns the source that spells it, so
24072        // navigation lands on the declaration and not on the macro envelope.
24073        for (name, expected) in [
24074            (
24075                "llama_new_context_with_model",
24076                "LLAMA_API struct llama_context * llama_new_context_with_model(",
24077            ),
24078            ("llama_tokenize", "LLAMA_API int32_t llama_tokenize("),
24079            ("llama_other", "LLAMA_API int32_t llama_other(int a)"),
24080        ] {
24081            let unit = parsed
24082                .declarations()
24083                .iter()
24084                .find(|unit| unit.is_function() && unit.fq_name() == name)
24085                .unwrap_or_else(|| panic!("missing recovered declaration {name}"));
24086            let [range] = parsed.declaration_ranges(unit) else {
24087                panic!("{name} must have exactly one range");
24088            };
24089            let text = &source[range.start_byte..range.end_byte];
24090            assert!(
24091                text.starts_with(expected),
24092                "{name} range is {text:?}, expected it to start with {expected:?}"
24093            );
24094            assert!(
24095                text.ends_with(')') || text.ends_with(';'),
24096                "{name}: {text:?}"
24097            );
24098        }
24099    }
24100
24101    /// whisper.cpp's own `include/whisper.h` writes the same construct with the
24102    /// invocation's `(` at the end of its line. The macro-declaration grammar
24103    /// keeps each invocation in its own declaration-scope `ERROR`: the wrapped
24104    /// declaration and the hint's exploded words sit in the parameter list,
24105    /// the invocation's `)` closes that list, and its `;` is left to the
24106    /// sibling `expression_statement`. (Before the macro-declaration grammar,
24107    /// the second invocation's envelope swallowed the third into a run of bare
24108    /// siblings; #3094 read that flattening from the token order, and the
24109    /// collapsed-run recovery still owns the envelope/statement pairing the
24110    /// same way.)
24111    ///
24112    /// The identifiers are `library_*` at whisper's own lengths on purpose:
24113    /// tree-sitter prices error recovery partly by how much input a repair
24114    /// skips, and the same construct written with shorter names parses cleanly.
24115    #[test]
24116    #[hardwon(
24117        "9e1f8732e419: C++ declaration recovery regressed for a flattened macro invocation run is read from its token order"
24118    )]
24119    fn a_flattened_macro_invocation_run_is_read_from_its_token_order() {
24120        let source = r#"    LIBRARY_DEPRECATED(
24121        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
24122        "use library_init_from_file_with_params instead"
24123    );
24124    LIBRARY_DEPRECATED(
24125        LIBRARY_API struct library_context * library_init_from_buffer(void * buffer, size_t buffer_size),
24126        "use library_init_from_buffer_with_params instead"
24127    );
24128    LIBRARY_DEPRECATED(
24129        LIBRARY_API struct library_context * library_init(struct library_model_loader * loader),
24130        "use library_init_with_params instead"
24131    );
24132"#;
24133        let mut parser = tree_sitter::Parser::new();
24134        parser
24135            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24136            .expect("C++ grammar");
24137        let tree = parser.parse(source, None).expect("C++ tree");
24138        let root = tree.root_node();
24139
24140        // The shape this recovery exists for: three sibling `ERROR` envelopes,
24141        // each holding one whole invocation, each followed by the statement
24142        // that owns the invocation's `;`.
24143        let mut cursor = root.walk();
24144        let items = root.children(&mut cursor).collect::<Vec<_>>();
24145        let [
24146            first,
24147            first_statement,
24148            second,
24149            second_statement,
24150            third,
24151            third_statement,
24152        ] = items.as_slice()
24153        else {
24154            panic!("{}", root.to_sexp());
24155        };
24156        let envelopes = [
24157            (first, first_statement),
24158            (second, second_statement),
24159            (third, third_statement),
24160        ];
24161        for (envelope, statement) in envelopes {
24162            assert_eq!(envelope.kind(), "ERROR", "{}", root.to_sexp());
24163            assert_eq!(
24164                statement.kind(),
24165                "expression_statement",
24166                "{}",
24167                root.to_sexp()
24168            );
24169            assert!(
24170                node_text(*envelope, source).ends_with(')'),
24171                "the invocation's own `)` closes its parameter list; the `;` is the statement's: {}",
24172                root.to_sexp()
24173            );
24174        }
24175
24176        // Each is admitted, and each names the byte just past its own `;`. A
24177        // self-contained invocation swallows nothing, so the recovery owns
24178        // only its own bytes.
24179        let ancestry = ParentIndex::new(root);
24180        let first_run = collapsed_macro_declaration_run(*first, source, &ancestry)
24181            .expect("the first invocation");
24182        assert_eq!(
24183            &source[..first_run.invocation_end],
24184            &source[..source.find("instead\"\n    );").expect("first hint")
24185                + "instead\"\n    );".len()]
24186        );
24187        for (envelope, _) in envelopes {
24188            let run = collapsed_macro_declaration_run(*envelope, source, &ancestry)
24189                .expect("each invocation");
24190            assert_eq!(
24191                run.region_end, run.invocation_end,
24192                "a self-contained invocation owns only its own bytes"
24193            );
24194        }
24195
24196        let parsed = parse_cpp_declarations(source, "library.h");
24197        assert_eq!(
24198            function_identities(&parsed),
24199            vec![
24200                (
24201                    "library_init".to_string(),
24202                    "(struct library_model_loader *)".to_string()
24203                ),
24204                (
24205                    "library_init_from_buffer".to_string(),
24206                    "(void *, size_t)".to_string()
24207                ),
24208                (
24209                    "library_init_from_file".to_string(),
24210                    "(const char *)".to_string()
24211                ),
24212            ],
24213            "{:#?}",
24214            parsed.declarations()
24215        );
24216    }
24217
24218    #[test]
24219    #[hardwon(
24220        "9e1f8732e419: C++ declaration recovery must not lift a conditional function-body error to file scope"
24221    )]
24222    fn a_conditional_error_inside_an_ordinary_function_is_not_global_scope() {
24223        let source = r#"void host() {
24224#ifdef ENABLED
24225LIBRARY_DEPRECATED(
24226    int hidden(const char* value),
24227    "hint"
24228);
24229#endif
24230}
24231"#;
24232        let mut parser = tree_sitter::Parser::new();
24233        parser
24234            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24235            .expect("C++ grammar");
24236        let tree = parser.parse(source, None).expect("C++ tree");
24237        let root = tree.root_node();
24238        let function = root.named_child(0).expect("ordinary function");
24239        let body = function
24240            .child_by_field_name("body")
24241            .expect("ordinary function body");
24242        let conditional = body.named_child(0).expect("conditional body");
24243        let mut conditional_cursor = conditional.walk();
24244        let envelope = conditional
24245            .named_children(&mut conditional_cursor)
24246            .find(|child| child.kind() == "ERROR")
24247            .expect("macro error");
24248        assert_eq!(conditional.kind(), "preproc_ifdef");
24249        assert_eq!(envelope.kind(), "ERROR");
24250
24251        let ancestry = ParentIndex::new(root);
24252        assert!(declaration_scope_container(envelope, &ancestry).is_none());
24253        assert!(collapsed_macro_declaration_run(envelope, source, &ancestry).is_none());
24254
24255        let parsed = parse_cpp_declarations(source, "function-body-macro.h");
24256        assert!(
24257            parsed
24258                .declarations()
24259                .iter()
24260                .all(|unit| unit.fq_name() != "hidden"),
24261            "a function-body macro error must not become a global declaration: {:#?}",
24262            parsed.declarations()
24263        );
24264    }
24265
24266    /// The hand-off that keeps the scan finite. An invocation that reaches the
24267    /// end of its own declaration scope swallowed nothing, and the ordinary
24268    /// `macro_wrapped_declarations` reader has it. The scan reparses exactly
24269    /// that region for every invocation it finds, so admitting it here would
24270    /// hand the scan its own input back.
24271    #[test]
24272    #[hardwon(
24273        "9e1f8732e419: C++ declaration recovery regressed for an invocation that fills its scope is left to the ordinary reader"
24274    )]
24275    fn an_invocation_that_fills_its_scope_is_left_to_the_ordinary_reader() {
24276        let source = r#"LIBRARY_DEPRECATED(
24277        LIBRARY_API struct library_context * library_init_from_file(const char * path_model),
24278        "use library_init_from_file_with_params instead"
24279    );"#;
24280        let mut parser = tree_sitter::Parser::new();
24281        parser
24282            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24283            .expect("C++ grammar");
24284        let tree = parser.parse(source, None).expect("C++ tree");
24285        let root = tree.root_node();
24286        let head = root.named_child(0).expect("the invocation");
24287        let ancestry = ParentIndex::new(root);
24288        assert!(
24289            collapsed_macro_declaration_run(head, source, &ancestry).is_none(),
24290            "{}",
24291            root.to_sexp()
24292        );
24293        assert!(
24294            !macro_wrapped_declarations(head, source, &ancestry).is_empty(),
24295            "the ordinary reader must be the one that has it: {}",
24296            root.to_sexp()
24297        );
24298        // The index is a speed substitution, never a semantic one: the
24299        // unindexed path is `Node::parent` itself and must agree.
24300        let unindexed = ParentIndex::unindexed();
24301        assert!(
24302            collapsed_macro_declaration_run(head, source, &unindexed).is_none(),
24303            "indexed and unindexed climbs must agree"
24304        );
24305        assert_eq!(
24306            macro_wrapped_declarations(head, source, &ancestry).len(),
24307            macro_wrapped_declarations(head, source, &unindexed).len(),
24308            "indexed and unindexed climbs must agree"
24309        );
24310    }
24311
24312    /// Negative controls for the same recovery: a macro invocation whose
24313    /// arguments are not a declaration recovers nothing, whether the parser
24314    /// keeps it clean, reads the arguments as a type, or reads them as a bare
24315    /// declarator.
24316    #[test]
24317    #[hardwon(
24318        "a0b3bd93484b: C++ declaration recovery regressed for a macro call without a wrapped declaration recovers nothing"
24319    )]
24320    fn a_macro_call_without_a_wrapped_declaration_recovers_nothing() {
24321        for source in [
24322            "int before;\nFOO(1, 2);\nint after;\n",
24323            "int before;\nMACRO(struct Foo, \"hint\");\nint after;\n",
24324            "int before;\nMACRO(int a, int b);\nint after;\n",
24325            "DECLARE_HANDLE(HWND);\nint after;\n",
24326        ] {
24327            let parsed = parse_cpp_declarations(source, "macro-call.h");
24328            assert_eq!(
24329                function_identities(&parsed),
24330                Vec::new(),
24331                "{source:?} must declare no function: {:#?}",
24332                parsed.declarations()
24333            );
24334        }
24335    }
24336
24337    /// #2552 shape 3, plain class. `BOTAN_DEPRECATED("text") explicit Ctor(T);`
24338    /// makes tree-sitter read the macro as the member's type and its argument
24339    /// list as a parenthesized declarator, which then swallows the attributed
24340    /// member and the member written after it. Both are members.
24341    #[test]
24342    #[hardwon(
24343        "c1aac56476f0: C++ declaration recovery regressed for a string attribute macro member keeps itself and the member after it"
24344    )]
24345    fn a_string_attribute_macro_member_keeps_itself_and_the_member_after_it() {
24346        let source = r#"#include <string_view>
24347namespace Botan {
24348class DL_Group final {
24349   public:
24350      DL_Group() = default;
24351      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
24352      DL_Group(std::string_view pem, int format);
24353      size_t get_p() const;
24354};
24355}
24356"#;
24357        let parsed = parse_cpp_declarations(source, "dl_group.h");
24358        assert_eq!(
24359            function_identities(&parsed),
24360            vec![
24361                ("Botan.DL_Group.DL_Group".to_string(), "()".to_string()),
24362                (
24363                    "Botan.DL_Group.DL_Group".to_string(),
24364                    "(std::string_view)".to_string()
24365                ),
24366                (
24367                    "Botan.DL_Group.DL_Group".to_string(),
24368                    "(std::string_view, int)".to_string()
24369                ),
24370                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
24371            ],
24372            "{:#?}",
24373            parsed.declarations()
24374        );
24375    }
24376
24377    /// #2552 shape 3, export-macro class. The class head macro makes the body a
24378    /// `compound_statement`, so members come from the region reparse, and one
24379    /// string-attribute member used to make that reparse unindexable -- which
24380    /// left the class with no members at all.
24381    #[test]
24382    #[hardwon(
24383        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps its string attribute members"
24384    )]
24385    fn an_export_macro_class_keeps_its_string_attribute_members() {
24386        let source = r#"#include <string_view>
24387namespace Botan {
24388class BOTAN_PUBLIC_API(2, 0) DL_Group final {
24389   public:
24390      BOTAN_DEPRECATED("Use DL_Group::from_name") explicit DL_Group(std::string_view name);
24391      DL_Group(std::string_view pem, int format);
24392      size_t get_p() const;
24393};
24394}
24395"#;
24396        let parsed = parse_cpp_declarations(source, "dl_group.h");
24397        assert!(
24398            parsed
24399                .declarations()
24400                .iter()
24401                .any(|unit| unit.is_class() && unit.fq_name() == "Botan.DL_Group"),
24402            "{:#?}",
24403            parsed.declarations()
24404        );
24405        assert_eq!(
24406            function_identities(&parsed),
24407            vec![
24408                (
24409                    "Botan.DL_Group.DL_Group".to_string(),
24410                    "(std::string_view)".to_string()
24411                ),
24412                (
24413                    "Botan.DL_Group.DL_Group".to_string(),
24414                    "(std::string_view, int)".to_string()
24415                ),
24416                ("Botan.DL_Group.get_p".to_string(), "() const".to_string()),
24417            ],
24418            "{:#?}",
24419            parsed.declarations()
24420        );
24421    }
24422
24423    /// #2552 shape 3, the `= default` variant plus the access-label
24424    /// constructor. The attributed defaulted constructor separates cleanly, but
24425    /// it strands the next member in a bare `ERROR`, and a constructor written
24426    /// with a member-initializer list under `private:` dissolves into
24427    /// expression soup that the reparse recovers from its own source range.
24428    #[test]
24429    #[hardwon(
24430        "c1aac56476f0: C++ declaration recovery regressed for an export macro class keeps stranded and access labeled constructors"
24431    )]
24432    fn an_export_macro_class_keeps_stranded_and_access_labeled_constructors() {
24433        let source = r#"namespace Botan {
24434class BOTAN_PUBLIC_API(2, 0) XMSS_Parameters final {
24435   public:
24436      BOTAN_DEPRECATED("Deprecated no replacement") XMSS_Parameters() = default;
24437      XMSS_Parameters(int oid, int len);
24438      size_t len() const;
24439
24440   private:
24441      XMSS_Parameters(int oid, int wots_oid, size_t hash_len, size_t tree_height) :
24442            m_oid(oid), m_wots_oid(wots_oid), m_element_size(hash_len), m_tree_height(tree_height) {}
24443
24444      int m_oid;
24445      int m_wots_oid;
24446      size_t m_element_size;
24447      size_t m_tree_height;
24448};
24449}
24450"#;
24451        let parsed = parse_cpp_declarations(source, "xmss_parameters.h");
24452        let constructors = function_identities(&parsed)
24453            .into_iter()
24454            .filter(|(name, _)| name == "Botan.XMSS_Parameters.XMSS_Parameters")
24455            .map(|(_, signature)| signature)
24456            .collect::<Vec<_>>();
24457        assert_eq!(
24458            constructors,
24459            vec![
24460                "()".to_string(),
24461                "(int, int)".to_string(),
24462                "(int, int, size_t, size_t)".to_string(),
24463            ],
24464            "{:#?}",
24465            parsed.declarations()
24466        );
24467    }
24468
24469    /// Negative control for the same rule: a real qualified name spells its
24470    /// `::` in the source, so the separator is present rather than MISSING and
24471    /// the out-of-line definition keeps its owner.
24472    #[test]
24473    #[hardwon(
24474        "6b1e2f76b805: C++ declaration recovery regressed for a genuine qualified out of line definition keeps its scope"
24475    )]
24476    fn a_genuine_qualified_out_of_line_definition_keeps_its_scope() {
24477        let source = r#"namespace shell {
24478struct Outer {
24479   struct Inner {
24480      Inner(int v);
24481      void run(int v);
24482   };
24483};
24484Outer::Inner::Inner(int v) {}
24485void Outer::Inner::run(int v) {}
24486}
24487"#;
24488        let parsed = parse_cpp_declarations(source, "outer.cpp");
24489        let names = function_identities(&parsed)
24490            .into_iter()
24491            .map(|(fq_name, _)| fq_name)
24492            .collect::<Vec<_>>();
24493        assert!(
24494            names
24495                .iter()
24496                .all(|name| name.starts_with("shell.Outer$Inner.")),
24497            "{names:#?}"
24498        );
24499    }
24500
24501    #[test]
24502    #[hardwon(
24503        "3aa210fd2699: C++ declaration recovery regressed for macro decorated template class keeps member scope without forward declaration"
24504    )]
24505    fn macro_decorated_template_class_keeps_member_scope_without_forward_declaration() {
24506        let source = r#"namespace control {
24507template <typename T>
24508class AnySpan;
24509template <typename T>
24510class ABSL_ATTRIBUTE_VIEW AnySpan {
24511 public:
24512  int begin() const;
24513};
24514}
24515
24516namespace absl {
24517ABSL_NAMESPACE_BEGIN
24518template <typename T>
24519class ABSL_ATTRIBUTE_VIEW Span {
24520 public:
24521  int begin() const;
24522  int back() const;
24523};
24524
24525int begin();
24526int back();
24527}
24528"#;
24529        let parsed = parse_cpp_declarations(source, "cpp-sentinel-span.cpp");
24530        let declarations = parsed.declarations();
24531        assert!(
24532            declarations
24533                .iter()
24534                .any(|unit| unit.is_class() && unit.fq_name() == "absl.Span")
24535        );
24536        for method in ["begin", "back"] {
24537            assert!(declarations.iter().any(|unit| {
24538                unit.is_function() && unit.fq_name() == format!("absl.Span.{method}")
24539            }));
24540            assert!(
24541                declarations.iter().any(|unit| {
24542                    unit.is_function() && unit.fq_name() == format!("absl.{method}")
24543                })
24544            );
24545        }
24546        assert!(
24547            declarations
24548                .iter()
24549                .any(|unit| unit.is_class() && unit.fq_name() == "control.AnySpan")
24550        );
24551        assert!(
24552            declarations
24553                .iter()
24554                .any(|unit| { unit.is_function() && unit.fq_name() == "control.AnySpan.begin" })
24555        );
24556        assert!(
24557            declarations
24558                .iter()
24559                .all(|unit| unit.fq_name() != "absl.ABSL_ATTRIBUTE_VIEW")
24560        );
24561    }
24562
24563    #[test]
24564    #[hardwon(
24565        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24566    )]
24567    fn explicit_global_member_definition_has_canonical_package_boundary() {
24568        let source = r#"
24569namespace arangodb::aql {
24570class ExecutionPlan {
24571 public:
24572  template<class... Args> Node* createNode(Args&&... args);
24573};
24574}
24575
24576template<class... Args>
24577Node* ::arangodb::aql::ExecutionPlan::createNode(Args&&... args) { return nullptr; }
24578"#;
24579        let parsed = parse_cpp_declarations(source, "global-member.cpp");
24580
24581        assert!(parsed.declarations().iter().any(|unit| {
24582            unit.is_function()
24583                && unit.package_name() == "arangodb::aql"
24584                && unit.short_name() == "ExecutionPlan.createNode"
24585                && unit.fq_name() == "arangodb::aql.ExecutionPlan.createNode"
24586        }));
24587    }
24588
24589    #[test]
24590    #[hardwon(
24591        "b020725e0ba9: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24592    )]
24593    fn consecutive_macro_export_classes_keep_namespace_sibling_ownership() {
24594        let source = r#"
24595#ifndef TINYXML2_INCLUDED
24596#define TINYXML2_INCLUDED
24597namespace tinyxml2 {
24598class TINYXML2_LIB XMLUtil {
24599 public:
24600  static const char* SkipWhiteSpace(const char* p) {
24601    while (*p) {
24602      if (*p == ' ') {
24603        ++p;
24604      }
24605    }
24606    return p;
24607  }
24608  static bool StringEqual(const char* p, const char* q) {
24609    return p == q;
24610  }
24611  class TINYXML2_LIB Helper {
24612   public:
24613    void Touch();
24614  };
24615  static void ToStr(int value, char* buffer);
24616 private:
24617  static const char* writeBoolTrue;
24618};
24619
24620class TINYXML2_LIB XMLNode {
24621 public:
24622  virtual XMLNode* ShallowClone() const = 0;
24623  virtual bool ShallowEqual(const XMLNode* compare) const = 0;
24624};
24625}
24626#endif
24627"#;
24628        let mut parser = tree_sitter::Parser::new();
24629        parser
24630            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24631            .unwrap();
24632        let tree = parser.parse(source, None).unwrap();
24633        let mut boundary_found = false;
24634        walk_named_tree_preorder(tree.root_node(), true, |node| {
24635            if let Some((_, name, _, _)) = recover_exported_class_function_definition(node, source)
24636                && name == "XMLUtil"
24637            {
24638                boundary_found = fragmented_export_sibling_class_boundary(
24639                    node,
24640                    source,
24641                    &ParentIndex::unindexed(),
24642                )
24643                .and_then(|boundary| recover_exported_class_function_definition(boundary, source))
24644                .is_some_and(|(_, name, _, _)| name == "XMLNode");
24645            }
24646            WalkControl::Continue
24647        });
24648        assert!(
24649            boundary_found,
24650            "fixture must exercise the recovered sibling boundary"
24651        );
24652
24653        let parsed = parse_cpp_declarations(source, "macro-sibling-classes.cpp");
24654        assert!(
24655            parsed
24656                .declarations()
24657                .iter()
24658                .any(|unit| unit.fq_name() == "tinyxml2.XMLNode"),
24659            "{:#?}",
24660            parsed.declarations()
24661        );
24662        assert!(
24663            parsed
24664                .declarations()
24665                .iter()
24666                .all(|unit| unit.fq_name() != "tinyxml2.XMLUtil$XMLNode"),
24667            "{:#?}",
24668            parsed.declarations()
24669        );
24670        assert!(parsed.declarations().iter().any(|unit| {
24671            unit.fq_name() == "tinyxml2.XMLNode.ShallowEqual" && unit.is_function()
24672        }));
24673        assert!(
24674            parsed
24675                .declarations()
24676                .iter()
24677                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil.ToStr" && unit.is_function() })
24678        );
24679        assert!(
24680            parsed
24681                .declarations()
24682                .iter()
24683                .any(|unit| { unit.fq_name() == "tinyxml2.XMLUtil$Helper" && unit.is_class() })
24684        );
24685    }
24686
24687    #[test]
24688    #[hardwon(
24689        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24690    )]
24691    fn explicit_global_namespace_recovery_does_not_duplicate_lexical_scope() {
24692        // Clang's diagnostic suite intentionally contains this ill-formed
24693        // spelling. The analyzer must retain the parser's explicit-global AST
24694        // boundary instead of constructing `cwg311::::cwg311::X`.
24695        let parsed = parse_cpp_declarations(
24696            r#"
24697namespace cwg311 {
24698namespace X { namespace Y {} }
24699namespace ::cwg311::X {}
24700}
24701"#,
24702            "explicit-global-namespace.cpp",
24703        );
24704
24705        assert!(parsed.declarations().iter().any(|unit| {
24706            unit.kind() == CodeUnitType::Module
24707                && unit.short_name() == "cwg311::X"
24708                && unit.fq_name() == "cwg311::X"
24709        }));
24710        assert!(
24711            parsed
24712                .declarations()
24713                .iter()
24714                .all(|unit| !unit.short_name().contains("::::")),
24715            "recovered namespace names must not retain empty scope components: {:#?}",
24716            parsed.declarations()
24717        );
24718    }
24719
24720    #[test]
24721    #[hardwon(
24722        "bac47d427848: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24723    )]
24724    fn repeated_scope_separator_does_not_create_empty_function_owner() {
24725        let scope = ScopeInfo {
24726            package_name: "X".to_string(),
24727            module: None,
24728            class_unit: None,
24729            template_signature: None,
24730            template_metadata: None,
24731            declarations_are_fields: false,
24732            recovered_specialization_member_scope: false,
24733            visible_using_namespaces: Vec::new(),
24734            local_block: None,
24735            visible_using_declarations: Arc::default(),
24736        };
24737
24738        let (owner, name, package) = split_cpp_name("X::::doit", &scope);
24739
24740        assert!(owner.is_none());
24741        assert_eq!(name, "doit");
24742        assert_eq!(package, "X");
24743    }
24744
24745    #[test]
24746    #[hardwon(
24747        "79f4bb1fb605: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24748    )]
24749    fn trailing_decltype_expression_is_not_a_function_declarator() {
24750        let source = r#"
24751namespace boost { namespace detail {
24752#if ! defined(BOOST_NO_SFINAE_EXPR) && \
24753    ! defined(BOOST_NO_CXX11_DECLTYPE) && \
24754    ! defined(BOOST_NO_CXX11_TRAILING_RESULT_TYPES)
24755#define BOOST_THREAD_PROVIDES_INVOKE
24756#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
24757template <class Fp, class A0, class ...Args>
24758inline auto
24759invoke(BOOST_THREAD_RV_REF(Fp) f, BOOST_THREAD_RV_REF(A0) a0,
24760       BOOST_THREAD_RV_REF(Args) ...args)
24761    -> decltype((boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...))
24762{
24763    return (boost::forward<A0>(a0).*f)(boost::forward<Args>(args)...);
24764}
24765#endif
24766#endif
24767}}
24768"#;
24769        let parsed = parse_cpp_declarations(source, "trailing-decltype.hpp");
24770
24771        assert!(
24772            parsed
24773                .declarations()
24774                .iter()
24775                .all(|unit| unit.short_name() != ".*f")
24776        );
24777    }
24778
24779    fn find_class_named<'tree>(
24780        root: Node<'tree>,
24781        source: &str,
24782        expected_name: &str,
24783    ) -> Option<Node<'tree>> {
24784        let mut stack = vec![root];
24785        while let Some(node) = stack.pop() {
24786            if node.kind() == "class_specifier"
24787                && node
24788                    .child_by_field_name("name")
24789                    .is_some_and(|name| node_text(name, source) == expected_name)
24790            {
24791                return Some(node);
24792            }
24793            let mut cursor = node.walk();
24794            stack.extend(node.named_children(&mut cursor));
24795        }
24796        None
24797    }
24798
24799    #[test]
24800    #[hardwon(
24801        "9ac72fd81fc1: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24802    )]
24803    fn sentinel_candidate_rejects_macro_qualified_callables_before_reparse() {
24804        let source = r#"EXPORT void definition(struct Value value) {}
24805EXPORT void prototype(struct Value value);
24806"#;
24807        let mut parser = tree_sitter::Parser::new();
24808        parser
24809            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24810            .unwrap();
24811        let tree = parser.parse(source, None).unwrap();
24812        let root = tree.root_node();
24813        let mut cursor = root.walk();
24814        let callables = root
24815            .named_children(&mut cursor)
24816            .filter(|node| matches!(node.kind(), "function_definition" | "declaration"))
24817            .collect::<Vec<_>>();
24818
24819        assert_eq!(callables.len(), 2, "unexpected fixture shape: {root}");
24820        for callable in callables {
24821            // The macro-declaration grammar parses these cleanly, with the
24822            // export macro retained as a `macro_modifier`; the preserved
24823            // callable and its `struct` parameter are what the sentinel gate
24824            // rejects on, with or without an ERROR in the envelope.
24825            assert!(
24826                cpp_sentinel_macro_parts(callable, source).is_none(),
24827                "macro-qualified callable must be rejected before sentinel region discovery: {callable}"
24828            );
24829        }
24830    }
24831
24832    #[test]
24833    #[hardwon(
24834        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24835    )]
24836    fn sentinel_candidate_keeps_class_before_recovered_member_callable() {
24837        let source = r#"namespace absl {
24838ABSL_NAMESPACE_BEGIN
24839// Generate a floating-point variate conforming to a Beta distribution:
24840template <typename RealType = double>
24841class beta_distribution {
24842 public:
24843  using result_type = RealType;
24844
24845
24846  beta_distribution() : beta_distribution(1) {}
24847
24848  explicit beta_distribution(result_type alpha, result_type beta = 1)
24849      : param_(alpha, beta) {}
24850
24851  explicit beta_distribution(const param_type& p) : param_(p) {}
24852
24853  void reset() {}
24854
24855  // Generating functions
24856  template <typename URBG>
24857  result_type operator()(URBG& g) {  // NOLINT(runtime/references)
24858    return (*this)(g, param_);
24859  }
24860
24861};
24862ABSL_NAMESPACE_END
24863}  // namespace absl
24864"#;
24865        let mut parser = tree_sitter::Parser::new();
24866        parser
24867            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24868            .unwrap();
24869        let tree = parser.parse(source, None).unwrap();
24870        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24871        let body = namespace
24872            .child_by_field_name("body")
24873            .expect("fixture namespace body");
24874        let sentinel = body.named_child(0).expect("sentinel envelope");
24875
24876        // The macro-declaration grammar keeps the sentinel head in an `ERROR`
24877        // envelope that ends at the class name, with the class body as the
24878        // sibling it recovered with; the bogus callable the older grammar
24879        // assembled from the last member is no longer part of the envelope.
24880        assert_eq!(sentinel.kind(), "ERROR");
24881        assert!(
24882            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24883            "a class preceding its recovered member callable remains a sentinel: {sentinel}"
24884        );
24885
24886        let parsed = parse_cpp_declarations(source, "beta-distribution.cpp");
24887        for expected in [
24888            "absl.beta_distribution",
24889            "absl.beta_distribution.beta_distribution",
24890            "absl.beta_distribution.reset",
24891        ] {
24892            assert!(
24893                parsed
24894                    .declarations()
24895                    .iter()
24896                    .any(|unit| unit.fq_name() == expected),
24897                "missing recovered declaration {expected}: {:#?}",
24898                parsed.declarations()
24899            );
24900        }
24901    }
24902
24903    #[test]
24904    #[hardwon(
24905        "b235b350b15c: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24906    )]
24907    fn sentinel_candidate_keeps_class_before_recovered_constructor_callable() {
24908        let source = r#"namespace absl {
24909ABSL_NAMESPACE_BEGIN
24910// absl::discrete_distribution
24911//
24912// A discrete distribution produces random integers i, where 0 <= i < n
24913template <typename IntType = int>
24914class discrete_distribution {
24915 public:
24916  using result_type = IntType;
24917  class param_type {
24918   public:
24919    param_type() { init(); }
24920    template <typename InputIterator>
24921    explicit param_type(InputIterator begin, InputIterator end)
24922        : p_(begin, end) {
24923      init();
24924    }
24925  };
24926  discrete_distribution() : param_() {}
24927  explicit discrete_distribution(const param_type& p) : param_(p) {}
24928};
24929ABSL_NAMESPACE_END
24930}  // namespace absl
24931"#;
24932        let mut parser = tree_sitter::Parser::new();
24933        parser
24934            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24935            .unwrap();
24936        let tree = parser.parse(source, None).unwrap();
24937        let namespace = tree.root_node().named_child(0).expect("fixture namespace");
24938        let body = namespace
24939            .child_by_field_name("body")
24940            .expect("fixture namespace body");
24941        let sentinel = body.named_child(0).expect("sentinel envelope");
24942        let callable = sentinel
24943            .child_by_field_name("declarator")
24944            .and_then(extract_function_declarator)
24945            .and_then(cpp_function_declarator_name_node)
24946            .expect("preserved callable name");
24947
24948        assert_eq!(sentinel.kind(), "function_definition");
24949        assert_eq!(callable.kind(), "identifier");
24950        assert!(
24951            cpp_sentinel_macro_parts(sentinel, source).is_some(),
24952            "a class preceding its recovered constructor remains a sentinel: {sentinel}"
24953        );
24954    }
24955
24956    #[test]
24957    #[hardwon(
24958        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
24959    )]
24960    fn macro_qualified_member_function_does_not_publish_namespace_as_field() {
24961        let source = r#"
24962#define CPPCHECKLIB
24963class Library {
24964    struct Container {
24965        CPPCHECKLIB static std::string toString(Yield yield);
24966        CPPCHECKLIB static std::string toString(Action action);
24967    };
24968};
24969"#;
24970        let mut parser = tree_sitter::Parser::new();
24971        parser
24972            .set_language(&tree_sitter_cpp::LANGUAGE.into())
24973            .unwrap();
24974        let tree = parser.parse(source, None).unwrap();
24975        let file = ProjectFile::new(std::env::temp_dir(), "macro-qualified-function.hpp");
24976        let parsed = parse_cpp_file(&file, source, &tree);
24977        assert!(
24978            parsed
24979                .declarations()
24980                .iter()
24981                .all(|unit| unit.fq_name() != "Library$Container.std"),
24982            "the qualified return-type namespace must not become a field: {:#?}",
24983            parsed.declarations()
24984        );
24985        for expected in ["(Yield)", "(Action)"] {
24986            assert!(
24987                parsed.declarations().iter().any(|unit| {
24988                    unit.is_function()
24989                        && unit.fq_name() == "Library$Container.toString"
24990                        && unit.signature() == Some(expected)
24991                }),
24992                "recovered toString overload {expected} is missing: {:#?}",
24993                parsed.declarations()
24994            );
24995        }
24996    }
24997
24998    #[test]
24999    #[hardwon(
25000        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25001    )]
25002    fn fragmented_export_constructor_keeps_initializer_names_as_fields() {
25003        let source = r#"
25004#define SIMPLECPP_LIB
25005namespace simplecpp {
25006using TokenString = std::string;
25007struct Location { int line{}; };
25008class SIMPLECPP_LIB Token {
25009  TokenString prefix;
25010  void prefix_method() {}
25011 public:
25012  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
25013      whitespaceahead(wsahead), location(loc), string(s)
25014      // The comment must not hide the constructor body from recovery.
25015      {
25016      flags();
25017  }
25018  TokenString string;
25019  bool whitespaceahead;
25020  Location location;
25021  Token *previous{};
25022 private:
25023  void flags() {
25024      whitespaceahead = true;
25025  }
25026};
25027}
25028"#;
25029        let parsed = parse_cpp_declarations(source, "fragmented-export-constructor.hpp");
25030
25031        let location_fields = parsed
25032            .declarations()
25033            .iter()
25034            .filter(|unit| unit.fq_name() == "simplecpp.Token.location")
25035            .collect::<Vec<_>>();
25036        assert_eq!(
25037            location_fields.len(),
25038            1,
25039            "location should have one class-owned declaration: {:#?}",
25040            parsed.declarations()
25041        );
25042        assert!(
25043            location_fields[0].is_field(),
25044            "location has wrong kind: {:#?}",
25045            parsed.declarations()
25046        );
25047        assert!(
25048            parsed.declarations().iter().all(|unit| {
25049                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.location")
25050            })
25051        );
25052        assert!(
25053            parsed.declarations().iter().all(|unit| {
25054                !(unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
25055            })
25056        );
25057        assert!(
25058            parsed
25059                .declarations()
25060                .iter()
25061                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.flags")
25062        );
25063        assert!(
25064            parsed
25065                .declarations()
25066                .iter()
25067                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token"),
25068            "the recovered class must retain its constructor: {:#?}",
25069            parsed.declarations()
25070        );
25071        assert!(
25072            parsed
25073                .declarations()
25074                .iter()
25075                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.prefix")
25076        );
25077        assert!(parsed.declarations().iter().any(|unit| {
25078            unit.is_function() && unit.fq_name() == "simplecpp.Token.prefix_method"
25079        }));
25080        let constructor = parsed
25081            .declarations()
25082            .iter()
25083            .find(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.Token")
25084            .expect("recovered constructor");
25085        let constructor_start = source.find("Token(const").expect("constructor start");
25086        let constructor_end = source
25087            .get(
25088                ..source
25089                    .find("  TokenString string;")
25090                    .expect("constructor end"),
25091            )
25092            .expect("constructor slice")
25093            .trim_end()
25094            .len();
25095        assert!(
25096            parsed
25097                .navigation_ranges
25098                .get(constructor)
25099                .is_some_and(|ranges| {
25100                    ranges.iter().any(|range| {
25101                        range.start_byte == constructor_start && range.end_byte == constructor_end
25102                    })
25103                }),
25104            "constructor navigation must span the full body: {:#?}",
25105            parsed.navigation_ranges
25106        );
25107        assert_eq!(
25108            parsed
25109                .signature_metadata
25110                .get(constructor)
25111                .and_then(|metadata| metadata.first())
25112                .and_then(SignatureMetadata::callable_linkage),
25113            Some(CallableLinkage::External)
25114        );
25115        let token_class = parsed
25116            .declarations()
25117            .iter()
25118            .find(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Token")
25119            .expect("recovered Token class");
25120        let class_end = source.rfind("};\n}").expect("class terminator") + 2;
25121        assert!(
25122            parsed
25123                .navigation_ranges
25124                .get(token_class)
25125                .is_some_and(|ranges| ranges.iter().any(|range| range.end_byte == class_end)),
25126            "class navigation must include the terminating semicolon: {:#?}",
25127            parsed.navigation_ranges
25128        );
25129    }
25130
25131    #[test]
25132    #[hardwon(
25133        "b28b8950890a: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25134    )]
25135    fn simplecpp_token_fragmented_export_keeps_location_and_string_fields() {
25136        let source = r#"
25137#define SIMPLECPP_LIB
25138namespace simplecpp {
25139using TokenString = std::string;
25140class Macro;
25141struct Location {
25142  unsigned int fileIndex{};
25143  unsigned int line{};
25144  unsigned int col{};
25145};
25146struct Output {
25147  int type;
25148};
25149class SIMPLECPP_LIB Token {
25150 public:
25151  Token(const TokenString &s, const Location &loc, bool wsahead = false) :
25152      whitespaceahead(wsahead), location(loc), string(s) {
25153      flags();
25154  }
25155  Token(const Token &tok) :
25156      macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name),
25157      number(tok.number), whitespaceahead(tok.whitespaceahead), location(tok.location),
25158      string(tok.string), mExpandedFrom(tok.mExpandedFrom) {}
25159  Token &operator=(const Token &tok) = delete;
25160  const TokenString& str() const { return string; }
25161  void setstr(const std::string &s) { string = s; flags(); }
25162  bool isOneOf(const char ops[]) const;
25163  TokenString macro;
25164  char op;
25165  bool comment;
25166  bool name;
25167  bool number;
25168  bool whitespaceahead;
25169  Location location;
25170  Token *previous{};
25171  Token *next{};
25172 private:
25173  void flags() {
25174      name = !string.empty();
25175      comment = false;
25176      number = false;
25177      op = 0;
25178  }
25179  TokenString string;
25180};
25181}
25182struct Following {
25183  int type;
25184};
25185class SIMPLECPP_LIB Later {
25186 public:
25187  Later(int value) : value(value) {}
25188  int value;
25189};
25190"#;
25191        let parsed = parse_cpp_declarations(source, "simplecpp-token.hpp");
25192        assert!(
25193            parsed
25194                .declarations()
25195                .iter()
25196                .any(|unit| { unit.is_field() && unit.fq_name() == "simplecpp.Token.location" })
25197        );
25198        assert!(
25199            !parsed
25200                .declarations()
25201                .iter()
25202                .any(|unit| { unit.is_function() && unit.fq_name() == "simplecpp.Token.location" })
25203        );
25204        assert!(
25205            parsed
25206                .declarations()
25207                .iter()
25208                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Token.string")
25209        );
25210        assert!(
25211            !parsed
25212                .declarations()
25213                .iter()
25214                .any(|unit| unit.is_function() && unit.fq_name() == "simplecpp.Token.string")
25215        );
25216        assert!(
25217            parsed
25218                .declarations()
25219                .iter()
25220                .any(|unit| unit.is_class() && unit.fq_name() == "simplecpp.Output")
25221        );
25222        assert!(
25223            parsed
25224                .declarations()
25225                .iter()
25226                .any(|unit| unit.is_field() && unit.fq_name() == "simplecpp.Output.type")
25227        );
25228        assert!(
25229            parsed
25230                .declarations()
25231                .iter()
25232                .any(|unit| unit.is_class() && unit.fq_name() == "Following")
25233        );
25234        assert!(
25235            parsed
25236                .declarations()
25237                .iter()
25238                .any(|unit| unit.is_field() && unit.fq_name() == "Following.type")
25239        );
25240        assert!(
25241            parsed
25242                .declarations()
25243                .iter()
25244                .any(|unit| unit.is_class() && unit.fq_name() == "Later")
25245        );
25246        assert!(
25247            parsed
25248                .declarations()
25249                .iter()
25250                .any(|unit| unit.is_field() && unit.fq_name() == "Later.value")
25251        );
25252        assert!(parsed.declarations().iter().all(|unit| {
25253            !matches!(
25254                unit.fq_name().as_str(),
25255                "simplecpp.Token.Following" | "simplecpp.Token.Later"
25256            )
25257        }));
25258        assert!(
25259            !parsed
25260                .declarations()
25261                .iter()
25262                .any(|unit| unit.fq_name() == "simplecpp.Token.Output"),
25263            "the following struct must remain outside the recovered Token class"
25264        );
25265    }
25266
25267    #[test]
25268    #[hardwon(
25269        "0be673ef7747: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25270    )]
25271    fn fragmented_export_constructor_in_anonymous_namespace_has_internal_linkage() {
25272        let source = r#"
25273#define SIMPLECPP_LIB
25274namespace {
25275namespace simplecpp {
25276using TokenString = std::string;
25277struct Location { int line{}; };
25278class SIMPLECPP_LIB HiddenToken {
25279 public:
25280  HiddenToken(const TokenString &s, const Location &loc) :
25281      location(loc), string(s) {
25282      flags();
25283  }
25284  TokenString string;
25285  Location location;
25286  HiddenToken *previous{};
25287 private:
25288  void flags() {}
25289};
25290}
25291}
25292"#;
25293        let parsed = parse_cpp_declarations(source, "fragmented-anonymous-constructor.hpp");
25294        let constructor = parsed
25295            .declarations()
25296            .iter()
25297            .find(|unit| unit.is_function() && unit.identifier() == "HiddenToken")
25298            .expect("recovered anonymous-namespace constructor");
25299        assert_eq!(
25300            parsed
25301                .signature_metadata
25302                .get(constructor)
25303                .and_then(|metadata| metadata.first())
25304                .and_then(SignatureMetadata::callable_linkage),
25305            Some(CallableLinkage::Internal)
25306        );
25307    }
25308
25309    #[test]
25310    #[hardwon(
25311        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25312    )]
25313    fn macro_qualified_static_field_keeps_real_declarator() {
25314        let source = r#"#define JSON_INLINE_VARIABLE
25315struct Reader {
25316static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1, other = 2;
25317static JSON_INLINE_VARIABLE constexpr std::size_t *pointer = nullptr;
25318static JSON_INLINE_VARIABLE constexpr std::size_t &reference = other;
25319};"#;
25320        let mut parser = tree_sitter::Parser::new();
25321        parser
25322            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25323            .unwrap();
25324        let tree = parser.parse(source, None).unwrap();
25325        let file = ProjectFile::new(std::env::temp_dir(), "macro-static-field.hpp");
25326        let parsed = parse_cpp_file(&file, source, &tree);
25327        for expected in [
25328            "Reader.npos",
25329            "Reader.other",
25330            "Reader.pointer",
25331            "Reader.reference",
25332        ] {
25333            assert!(
25334                parsed
25335                    .declarations()
25336                    .iter()
25337                    .any(|unit| unit.is_field() && unit.fq_name() == expected),
25338                "real macro-decorated field {expected} is missing: {:#?}",
25339                parsed.declarations()
25340            );
25341        }
25342        assert!(
25343            parsed
25344                .declarations()
25345                .iter()
25346                .all(|unit| unit.fq_name() != "Reader.std"),
25347            "qualified type prefix became a pseudo-field: {:#?}",
25348            parsed.declarations()
25349        );
25350        let root = tree.root_node();
25351        let mut stack = vec![root];
25352        let mut signatures = Vec::new();
25353        // The macro-declaration grammar keeps `JSON_INLINE_VARIABLE` as a
25354        // `macro_modifier`, so the fields arrive as ordinary clean field
25355        // declarations whose declarators carry the signature text the
25356        // bitfield-clause recovery used to reconstruct.
25357        while let Some(current) = stack.pop() {
25358            if current.kind() == "field_declaration" {
25359                let mut cursor = current.walk();
25360                for declarator in current.children_by_field_name("declarator", &mut cursor) {
25361                    signatures.push(render_cpp_field_signature(current, declarator, source));
25362                }
25363            }
25364            let mut cursor = current.walk();
25365            stack.extend(current.named_children(&mut cursor));
25366        }
25367        signatures.sort();
25368        assert_eq!(
25369            signatures,
25370            [
25371                "static JSON_INLINE_VARIABLE constexpr std::size_t & reference = other;",
25372                "static JSON_INLINE_VARIABLE constexpr std::size_t * pointer = nullptr;",
25373                "static JSON_INLINE_VARIABLE constexpr std::size_t npos = 1;",
25374                "static JSON_INLINE_VARIABLE constexpr std::size_t other = 2;",
25375            ]
25376        );
25377    }
25378
25379    fn member_function_linkage(source: &str) -> CallableLinkage {
25380        let mut parser = tree_sitter::Parser::new();
25381        parser
25382            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25383            .unwrap();
25384        let tree = parser.parse(source, None).unwrap();
25385        let ancestry = ParentIndex::new(tree.root_node());
25386        let mut stack = vec![tree.root_node()];
25387        while let Some(node) = stack.pop() {
25388            if node.kind() == "function_definition" {
25389                let mut current = node.parent();
25390                while let Some(parent) = current {
25391                    if matches!(
25392                        parent.kind(),
25393                        "class_specifier" | "struct_specifier" | "union_specifier"
25394                    ) {
25395                        return cpp_callable_linkage(node, source, &ancestry);
25396                    }
25397                    current = parent.parent();
25398                }
25399            }
25400            let mut cursor = node.walk();
25401            stack.extend(node.named_children(&mut cursor));
25402        }
25403        panic!("fixture has no member function definition");
25404    }
25405
25406    #[test]
25407    fn cpp_member_linkage_source_scopes_local_and_unnamed_types() {
25408        assert_eq!(
25409            member_function_linkage("struct Named { int method() { return 1; } };"),
25410            CallableLinkage::External
25411        );
25412        assert_eq!(
25413            member_function_linkage(
25414                "int outer() { struct Local { int method() { return 1; } }; return 0; }"
25415            ),
25416            CallableLinkage::Internal
25417        );
25418        assert_eq!(
25419            member_function_linkage("struct { int method() { return 1; } } instance;"),
25420            CallableLinkage::Internal
25421        );
25422        assert_eq!(
25423            member_function_linkage("namespace { struct Named { int method() { return 1; } }; }"),
25424            CallableLinkage::Internal
25425        );
25426    }
25427
25428    #[test]
25429    #[hardwon(
25430        "fe77a0120e5e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25431    )]
25432    fn malformed_class_macro_constructors_have_no_decorator_return_type() {
25433        let source = r#"
25434#ifndef PROTON_VALUE_HPP
25435#define PROTON_VALUE_HPP
25436namespace proton {
25437namespace internal {
25438class value_base {
25439  protected:
25440    internal::data& data();
25441    internal::data data_;
25442  friend class codec::encoder;
25443  friend class codec::decoder;
25444};
25445}
25446class value : public internal::value_base, private internal::comparable<value> {
25447  private:
25448    template<class T, class U=void> struct assignable :
25449        public std::enable_if<codec::is_encodable<T>::value, U> {};
25450    template<class U> struct assignable<value, U> {};
25451  public:
25452    PN_CPP_EXTERN value();
25453    PN_CPP_EXTERN value(const value&);
25454    PN_CPP_EXTERN value& operator=(const value&);
25455    PN_CPP_EXTERN value(value&&);
25456    PN_CPP_EXTERN value& operator=(value&&);
25457    template <class T> value(const T& x, typename assignable<T>::type* = 0) { *this = x; }
25458    template <class T> typename assignable<T, value&>::type operator=(const T& x) {
25459        codec::encoder e(*this);
25460        e << x;
25461        return *this;
25462    }
25463    PN_CPP_EXTERN type_id type() const;
25464    PN_CPP_EXTERN bool empty() const;
25465    PN_CPP_EXTERN void clear();
25466    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") void get(T &t) const;
25467    template<class T> PN_CPP_DEPRECATED("Use 'proton::get'") T get() const;
25468  friend PN_CPP_EXTERN void swap(value&, value&);
25469  friend PN_CPP_EXTERN bool operator==(const value& x, const value& y);
25470  friend PN_CPP_EXTERN bool operator<(const value& x, const value& y);
25471  friend PN_CPP_EXTERN std::ostream& operator<<(std::ostream&, const value&);
25472    value(pn_data_t* d);
25473    void reset(pn_data_t* d = 0);
25474};
25475}
25476#endif
25477"#;
25478        let mut parser = tree_sitter::Parser::new();
25479        parser
25480            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25481            .unwrap();
25482        let tree = parser.parse(source, None).unwrap();
25483        let file = ProjectFile::new(std::env::temp_dir(), "qpid-value.hpp");
25484        let parsed = parse_cpp_file(&file, source, &tree);
25485        // The macro-declaration grammar parses the class and its decorated
25486        // members cleanly, so the constructors mint with their true
25487        // class-owned identity (`proton.value.value`) rather than the
25488        // namespace-scope identity the fragment recovery produced before.
25489        let macro_constructors = parsed
25490            .signature_metadata
25491            .iter()
25492            .filter(|(unit, _)| unit.is_function() && unit.fq_name() == "proton.value.value")
25493            .flat_map(|(_, metadata)| metadata)
25494            .filter(|metadata| metadata.label().starts_with("PN_CPP_EXTERN value("))
25495            .collect::<Vec<_>>();
25496
25497        assert_eq!(
25498            macro_constructors.len(),
25499            3,
25500            "fixture must retain the three macro-decorated constructor declarations: {:#?}",
25501            parsed.declarations()
25502        );
25503        assert!(
25504            macro_constructors.iter().all(|metadata| {
25505                metadata.return_type_text().is_none() && metadata.return_type_identity().is_none()
25506            }),
25507            "the export decorator is not a semantic constructor return type or identity: {macro_constructors:#?}"
25508        );
25509    }
25510
25511    #[test]
25512    #[hardwon(
25513        "c53d3b9c1b19: C++ declaration recovery regressed for recovered export class typedef uses displaced alias name"
25514    )]
25515    fn recovered_export_class_typedef_uses_displaced_alias_name() {
25516        let source = r#"
25517namespace spi {
25518class Filter {
25519public:
25520    enum FilterDecision { DENY, NEUTRAL, ACCEPT };
25521};
25522}
25523namespace filter {
25524class LOG4CXX_EXPORT LevelRangeFilter : public spi::Filter
25525{
25526public:
25527    typedef spi::Filter BASE_CLASS;
25528    DECLARE_LOG4CXX_OBJECT(LevelRangeFilter)
25529    BEGIN_LOG4CXX_CAST_MAP()
25530    LOG4CXX_CAST_ENTRY(LevelRangeFilter)
25531    LOG4CXX_CAST_ENTRY_CHAIN(BASE_CLASS)
25532    END_LOG4CXX_CAST_MAP()
25533    FilterDecision decide() const;
25534};
25535}
25536"#;
25537        let mut parser = tree_sitter::Parser::new();
25538        parser
25539            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25540            .unwrap();
25541        let tree = parser.parse(source, None).unwrap();
25542        let file = ProjectFile::new(std::env::temp_dir(), "log4cxx-typedef.cpp");
25543        let parsed = parse_cpp_file(&file, source, &tree);
25544        assert!(
25545            parsed.declarations().iter().any(|unit| {
25546                unit.is_class()
25547                    && unit.fq_name() == "filter.LevelRangeFilter$BASE_CLASS"
25548                    && unit.signature() == Some("typedef spi::Filter BASE_CLASS;")
25549            }),
25550            "the displaced typedef alias must retain its declared name: {:#?}",
25551            parsed.declarations()
25552        );
25553        assert!(
25554            parsed
25555                .declarations()
25556                .iter()
25557                .all(|unit| unit.fq_name() != "filter.LevelRangeFilter$Filter"),
25558            "the qualified underlying type must not become a false nested alias: {:#?}",
25559            parsed.declarations()
25560        );
25561    }
25562
25563    #[test]
25564    #[hardwon(
25565        "f7a2bb562e28: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25566    )]
25567    fn exported_single_base_recovery_uses_displaced_class_name() {
25568        let source = r#"
25569class CORE_EXPORT QgsPoint : public AbstractGeometry
25570{
25571    Q_GADGET
25572
25573    Q_PROPERTY( double x READ x WRITE setX )
25574    Q_PROPERTY( double y READ y WRITE setY )
25575    Q_PROPERTY( double z READ z WRITE setZ )
25576    Q_PROPERTY( double m READ m WRITE setM )
25577
25578  public:
25579#ifndef SIP_RUN
25580    QgsPoint(
25581      double x = std::numeric_limits<double>::quiet_NaN(),
25582      double y = std::numeric_limits<double>::quiet_NaN(),
25583      double z = std::numeric_limits<double>::quiet_NaN(),
25584      double m = std::numeric_limits<double>::quiet_NaN(),
25585      Qgis::WkbType wkbType = Qgis::WkbType::Unknown
25586    );
25587#else
25588    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 )];
25589    % MethodCode
25590    if ( sipCanConvertToType( a0, sipType_QgsPointXY, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25591    {
25592      int state;
25593      sipIsErr = 0;
25594      QgsPointXY *p = reinterpret_cast<QgsPointXY *>( sipConvertToType( a0, sipType_QgsPointXY, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25595      if ( !sipIsErr )
25596      {
25597        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25598      }
25599      sipReleaseType( p, sipType_QgsPointXY, state );
25600    }
25601    else if ( sipCanConvertToType( a0, sipType_QPointF, SIP_NOT_NONE ) && a1 == Py_None && a2 == Py_None && a3 == Py_None && a4 == Py_None )
25602    {
25603      int state;
25604      sipIsErr = 0;
25605
25606      QPointF *p = reinterpret_cast<QPointF *>( sipConvertToType( a0, sipType_QPointF, 0, SIP_NOT_NONE, &state, &sipIsErr ) );
25607      if ( !sipIsErr )
25608      {
25609        sipCpp = new sipQgsPoint( QgsPoint( *p ) );
25610      }
25611      sipReleaseType( p, sipType_QPointF, state );
25612    }
25613    else if (
25614      ( a0 == Py_None || PyFloat_AsDouble( a0 ) != -1.0 || !PyErr_Occurred() ) &&
25615      ( a1 == Py_None || PyFloat_AsDouble( a1 ) != -1.0 || !PyErr_Occurred() ) &&
25616      ( a2 == Py_None || PyFloat_AsDouble( a2 ) != -1.0 || !PyErr_Occurred() ) &&
25617      ( a3 == Py_None || PyFloat_AsDouble( a3 ) != -1.0 || !PyErr_Occurred() ) )
25618    {
25619      double x = a0 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a0 );
25620      double y = a1 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a1 );
25621      double z = a2 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a2 );
25622      double m = a3 == Py_None ? std::numeric_limits<double>::quiet_NaN() : PyFloat_AsDouble( a3 );
25623      Qgis::WkbType wkbType = a4 == Py_None ? Qgis::WkbType::Unknown : static_cast<Qgis::WkbType>( sipConvertToEnum( a4, sipType_Qgis_WkbType ) );
25624      sipCpp = new sipQgsPoint( QgsPoint( x, y, z, m, wkbType ) );
25625    }
25626    else // Invalid ctor arguments
25627    {
25628      PyErr_SetString( PyExc_TypeError, u"Invalid type in constructor arguments."_s.toUtf8().constData() );
25629      sipIsErr = 1;
25630    }
25631    % End
25632#endif
25633
25634    explicit QgsPoint( const QgsPointXY &p ) SIP_SKIP;
25635    explicit QgsPoint( QPointF p ) SIP_SKIP;
25636    explicit QgsPoint(
25637      Qgis::WkbType wkbType,
25638      double x = std::numeric_limits<double>::quiet_NaN(),
25639      double y = std::numeric_limits<double>::quiet_NaN(),
25640      double z = std::numeric_limits<double>::quiet_NaN(),
25641      double m = std::numeric_limits<double>::quiet_NaN()
25642    ) SIP_SKIP;
25643    explicit QgsPoint( const QVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25644    explicit QgsPoint( const QVector4D &vect ) SIP_SKIP;
25645    explicit QgsPoint( const QgsVector3D &vect, double m = std::numeric_limits<double>::quiet_NaN() ) SIP_SKIP;
25646#ifndef SIP_RUN
25647  private:
25648    bool fuzzyHelper(
25649      double epsilon,
25650      const AbstractGeometry &other,
25651      bool is3DFlag,
25652      bool isMeasureFlag
25653    ) const
25654    {
25655      return is3DFlag && isMeasureFlag && epsilon > 0 && &other;
25656    }
25657#endif
25658};
25659class Ordinary : public Base { public: Ordinary(); };
25660class API_EXPORT Plain { public: Plain(); };
25661class API_EXPORT : public Base {};
25662class
25663PN_CPP_CLASS_EXTERN Sender : public Link {
25664    Sender();
25665    struct impl;
25666    struct impl& get_impl() const;
25667};
25668class thread_ctx_t {};
25669class ctx_t ZMQ_FINAL : public thread_ctx_t {
25670    bool start();
25671};
25672"#;
25673        let mut parser = tree_sitter::Parser::new();
25674        parser
25675            .set_language(&tree_sitter_cpp::LANGUAGE.into())
25676            .unwrap();
25677        let tree = parser.parse(source, None).unwrap();
25678        let file = ProjectFile::new(std::env::temp_dir(), "exported-single-base.cpp");
25679        let parsed = parse_cpp_file(&file, source, &tree);
25680        let declarations = parsed.declarations();
25681
25682        for expected in ["QgsPoint", "Ordinary", "Plain", "Sender", "ctx_t"] {
25683            assert!(
25684                declarations
25685                    .iter()
25686                    .any(|unit| unit.is_class() && unit.fq_name() == expected),
25687                "missing recovered class {expected}: {declarations:#?}"
25688            );
25689        }
25690        let qgs_point = declarations
25691            .iter()
25692            .find(|unit| unit.is_class() && unit.fq_name() == "QgsPoint")
25693            .expect("recovered QgsPoint class");
25694        assert_eq!(
25695            parsed.raw_supertypes.get(qgs_point),
25696            Some(&vec!["AbstractGeometry".to_string()]),
25697            "single-base export recovery must retain its displaced base"
25698        );
25699        let ordinary_start = source.find("class Ordinary").expect("ordinary sibling");
25700        assert!(
25701            parsed
25702                .navigation_ranges
25703                .get(qgs_point)
25704                .is_some_and(|ranges| {
25705                    !ranges.is_empty()
25706                        && ranges.iter().all(|range| range.end_byte <= ordinary_start)
25707                }),
25708            "a rejected fragmented-body candidate must not leak a range across sibling classes: {:#?}",
25709            parsed.navigation_ranges.get(qgs_point)
25710        );
25711        let sender = declarations
25712            .iter()
25713            .find(|unit| unit.is_class() && unit.fq_name() == "Sender")
25714            .expect("recovered Sender class");
25715        assert_eq!(
25716            parsed.raw_supertypes.get(sender),
25717            Some(&vec!["Link".to_string()]),
25718            "post-declarator export recovery must retain its displaced base"
25719        );
25720        let recovered_member = declarations
25721            .iter()
25722            .find(|unit| unit.is_function() && unit.fq_name() == "Sender.get_impl")
25723            .unwrap_or_else(|| panic!("missing recovered Sender member: {declarations:#?}"));
25724        assert_eq!(
25725            parsed
25726                .signature_metadata
25727                .get(recovered_member)
25728                .and_then(|metadata| metadata.first())
25729                .and_then(SignatureMetadata::callable_linkage),
25730            Some(CallableLinkage::External),
25731            "a named recovered class's members have external linkage"
25732        );
25733        let ctx = declarations
25734            .iter()
25735            .find(|unit| unit.is_class() && unit.fq_name() == "ctx_t")
25736            .expect("recovered ctx_t class");
25737        assert_eq!(
25738            parsed.raw_supertypes.get(ctx),
25739            Some(&vec!["thread_ctx_t".to_string()]),
25740            "postfix export-macro recovery must retain its displaced base"
25741        );
25742        assert!(
25743            declarations.iter().any(|unit| {
25744                unit.is_function()
25745                    && unit.fq_name() == "QgsPoint.QgsPoint"
25746                    && unit.signature() == Some("(double, double, double, double, Qgis::WkbType)")
25747            }),
25748            "the conditional default donor must retain the recovered QgsPoint owner: {declarations:#?}"
25749        );
25750        assert!(
25751            declarations.iter().all(|unit| {
25752                !unit.is_class() || !matches!(unit.fq_name().as_str(), "AbstractGeometry" | "Base")
25753            }),
25754            "base declarators and an export macro without a displaced identifier must not become class identities: {declarations:#?}"
25755        );
25756    }
25757
25758    #[test]
25759    #[hardwon(
25760        "364341b21a48: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
25761    )]
25762    fn function_like_export_macro_classes_keep_names_and_base_edges() {
25763        // Every head shape after `class MACRO(2, 0)`: a plain body, a single
25764        // base, `final` without a base, `final` with one base, and `final` with
25765        // a comma-separated base list. Older grammars left each in its own
25766        // error shape; brokk-tree-sitter-cpp 0.24.7 reads them all as
25767        // `class_specifier`s, which publish an empty base list for a class
25768        // without bases.
25769        let source = r#"
25770namespace api {
25771class PROJECT_PUBLIC_API(2, 0) Prelude {
25772  public:
25773    Prelude();
25774};
25775class PROJECT_PUBLIC_API(2, 0) Base {
25776  public:
25777    Base(int value);
25778};
25779class PROJECT_PUBLIC_API(2, 0) Mixin {
25780  public:
25781    Mixin();
25782};
25783class PROJECT_PUBLIC_API(2, 0) Adopted : public Base {
25784  public:
25785    Adopted(int value);
25786};
25787class PROJECT_PUBLIC_API(2, 0) Derived final : public Base {
25788  public:
25789    Derived(int value);
25790};
25791class PROJECT_PUBLIC_API(2, 0) Solo final {
25792  public:
25793    Solo();
25794};
25795class PROJECT_PUBLIC_API(2, 0) Blended final : public Base, public Mixin {
25796  public:
25797    Blended(int value);
25798};
25799class PROJECT_PUBLIC_API(2, 0) Woven : public Base, public Mixin {
25800  public:
25801    Woven(int value);
25802};
25803} // namespace api
25804"#;
25805        let parsed = parse_cpp_declarations(source, "function-like-export.hpp");
25806        let explicit = parse_cpp_declarations(
25807            &source.replace("PROJECT_PUBLIC_API(2, 0) ", ""),
25808            "function-like-export.hpp",
25809        );
25810        assert_eq!(
25811            declaration_expansion_inventory(&parsed),
25812            declaration_expansion_inventory(&explicit)
25813        );
25814        let declarations = parsed.declarations();
25815        let class_named = |name: &str| {
25816            declarations
25817                .iter()
25818                .find(|unit| unit.is_class() && unit.fq_name() == name)
25819                .unwrap_or_else(|| {
25820                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25821                })
25822        };
25823        let base = class_named("api.Base");
25824        class_named("api.Prelude");
25825        class_named("api.Mixin");
25826
25827        assert_eq!(
25828            parsed.raw_supertypes.get(class_named("api.Adopted")),
25829            Some(&vec!["Base".to_string()])
25830        );
25831        assert_eq!(
25832            parsed.raw_supertypes.get(class_named("api.Derived")),
25833            Some(&vec!["Base".to_string()])
25834        );
25835        // The explicit expansion is the oracle for a class without bases: a
25836        // parsed class publishes an empty base list where recovery published
25837        // none.
25838        let solo = parsed.raw_supertypes.get(class_named("api.Solo"));
25839        assert_eq!(
25840            solo,
25841            explicit.raw_supertypes.get(class_named("api.Solo")),
25842            "a final class without a base list must match its explicit expansion"
25843        );
25844        assert!(
25845            solo.is_none_or(Vec::is_empty),
25846            "a final class without a base list must not invent a supertype"
25847        );
25848        assert_eq!(
25849            parsed.raw_supertypes.get(class_named("api.Blended")),
25850            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25851        );
25852        assert_eq!(
25853            parsed.raw_supertypes.get(class_named("api.Woven")),
25854            Some(&vec!["Base".to_string(), "Mixin".to_string()])
25855        );
25856        assert!(
25857            declarations
25858                .iter()
25859                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
25860            "the export macro must not become a declaration: {declarations:#?}"
25861        );
25862        assert!(
25863            declarations.iter().all(|unit| !matches!(
25864                unit.identifier(),
25865                "final" | "public" | "protected" | "private"
25866            )),
25867            "the head specifiers must not become declarations: {declarations:#?}"
25868        );
25869        assert!(
25870            parsed
25871                .navigation_ranges
25872                .get(base)
25873                .is_some_and(|ranges| !ranges.is_empty()),
25874            "the recovered base must retain a navigable declaration range"
25875        );
25876    }
25877
25878    #[test]
25879    #[hardwon(
25880        "861f46399c96: C++ declaration recovery regressed for function like export macro classes are named by position not spelling"
25881    )]
25882    fn function_like_export_macro_classes_are_named_by_position_not_spelling() {
25883        // #2557: the class name is the last identifier before the head ends
25884        // (`final`, the base clause `:`, or the body), whatever its spelling.
25885        // A class named in capitals (`X509_CA`) is a class, and an object-like
25886        // macro before the name (`OTHER_MACRO Name`) is decoration. `Name` is
25887        // the control whose spelling never mattered.
25888        let source = r#"
25889namespace api {
25890class PROJECT_PUBLIC_API(2, 0) Base {
25891  public:
25892    Base();
25893};
25894class PROJECT_PUBLIC_API(2, 0) Mixin {
25895  public:
25896    Mixin();
25897};
25898class PROJECT_PUBLIC_API(2, 0) Name {
25899  public:
25900    Name();
25901};
25902class PROJECT_PUBLIC_API(2, 0) X509_CA final {
25903  public:
25904    X509_CA();
25905};
25906class PROJECT_PUBLIC_API(2, 0) HSS_LMS_KEY final : public Base, public Mixin {
25907  public:
25908    HSS_LMS_KEY();
25909};
25910class PROJECT_PUBLIC_API(2, 0) GOST_3410 : public Base {
25911  public:
25912    GOST_3410();
25913};
25914class PROJECT_PUBLIC_API(2, 0) PKCS11_RSA {
25915  public:
25916    PKCS11_RSA();
25917};
25918class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Plain {
25919  public:
25920    Plain();
25921};
25922class PROJECT_PUBLIC_API(2, 0) OTHER_MACRO Decorated final : public Base {
25923  public:
25924    Decorated();
25925};
25926class PROJECT_PUBLIC_API(2, 0) FIRST_MACRO SECOND_MACRO Layered final : public Base, public Mixin {
25927  public:
25928    Layered();
25929};
25930} // namespace api
25931"#;
25932        let parsed = parse_cpp_declarations(source, "positional-export.hpp");
25933        let explicit = parse_cpp_declarations(
25934            &source
25935                .replace("PROJECT_PUBLIC_API(2, 0) ", "")
25936                .replace("FIRST_MACRO SECOND_MACRO ", "")
25937                .replace("OTHER_MACRO ", ""),
25938            "positional-export.hpp",
25939        );
25940        assert_eq!(
25941            declaration_expansion_inventory(&parsed),
25942            declaration_expansion_inventory(&explicit)
25943        );
25944        let declarations = parsed.declarations();
25945        let class_named = |name: &str| {
25946            declarations
25947                .iter()
25948                .find(|unit| unit.is_class() && unit.fq_name() == name)
25949                .unwrap_or_else(|| {
25950                    panic!("missing function-like export macro class {name}: {declarations:#?}")
25951                })
25952        };
25953        for (name, bases) in [
25954            ("api.Name", None),
25955            ("api.X509_CA", None),
25956            ("api.HSS_LMS_KEY", Some(vec!["Base", "Mixin"])),
25957            ("api.GOST_3410", Some(vec!["Base"])),
25958            ("api.PKCS11_RSA", None),
25959            ("api.Plain", None),
25960            ("api.Decorated", Some(vec!["Base"])),
25961            ("api.Layered", Some(vec!["Base", "Mixin"])),
25962        ] {
25963            let actual = parsed.raw_supertypes.get(class_named(name));
25964            match bases {
25965                Some(bases) => assert_eq!(
25966                    actual,
25967                    Some(&bases.into_iter().map(str::to_string).collect::<Vec<_>>()),
25968                    "{name}"
25969                ),
25970                // `MACRO(2, 0) OTHER_MACRO Plain {` without a base still reaches
25971                // recovery, which publishes no base list.
25972                None if name == "api.Plain" => assert_eq!(actual, None, "{name}"),
25973                // A head the grammar reads publishes the empty base list of
25974                // its explicit expansion.
25975                None => {
25976                    assert_eq!(
25977                        actual,
25978                        explicit.raw_supertypes.get(class_named(name)),
25979                        "{name} must match its explicit expansion"
25980                    );
25981                    assert!(actual.is_none_or(Vec::is_empty), "{name}");
25982                }
25983            }
25984        }
25985        assert!(
25986            declarations.iter().all(|unit| !matches!(
25987                unit.identifier(),
25988                "PROJECT_PUBLIC_API"
25989                    | "OTHER_MACRO"
25990                    | "FIRST_MACRO"
25991                    | "SECOND_MACRO"
25992                    | "final"
25993                    | "public"
25994            )),
25995            "macros and head specifiers must not become declarations: {declarations:#?}"
25996        );
25997    }
25998
25999    #[test]
26000    #[hardwon(
26001        "7552b3145622: C++ declaration recovery regressed for export class head with a virtual base recovers its fragmented body"
26002    )]
26003    fn export_class_head_with_a_virtual_base_recovers_its_fragmented_body() {
26004        // #2924: `class MACRO(2, 0) Name : public virtual Base {` leaves the
26005        // head, the body's `{` and its first member in one declaration-scope
26006        // ERROR, scatters the remaining members across the container's
26007        // siblings, and ends the container on the class's own `}`. The class
26008        // was lost and the macro minted a class of its own.
26009        let source = r#"
26010namespace api {
26011
26012/**
26013* Doc comment
26014*/
26015class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
26016   public:
26017      /**
26018      * Construct from a point.
26019      */
26020      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
26021
26022#if defined(PROJECT_HAS_LEGACY_POINT)
26023      /**
26024      * Construct from a legacy point.
26025      */
26026      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
26027#endif
26028
26029      std::string algo_name() const override;
26030
26031      AlgorithmIdentifier algorithm_identifier() const override;
26032};
26033
26034}
26035"#;
26036        let parsed = parse_cpp_declarations(source, "virtual-base.hpp");
26037        let declarations = parsed.declarations();
26038        let class = declarations
26039            .iter()
26040            .find(|unit| unit.is_class() && unit.fq_name() == "api.VirtualBased")
26041            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26042        assert_eq!(
26043            parsed.raw_supertypes.get(class),
26044            Some(&vec!["BaseKey".to_string()]),
26045            "the virtual base is the class's base: {declarations:#?}"
26046        );
26047        for member in [
26048            "api.VirtualBased.algo_name",
26049            "api.VirtualBased.algorithm_identifier",
26050        ] {
26051            assert!(
26052                declarations
26053                    .iter()
26054                    .any(|unit| unit.is_function() && unit.fq_name() == member),
26055                "{member} must be owned by the recovered class: {declarations:#?}"
26056            );
26057        }
26058        assert!(
26059            declarations
26060                .iter()
26061                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26062            "an unrecovered head must not mint a macro-named class: {declarations:#?}"
26063        );
26064    }
26065
26066    #[test]
26067    #[hardwon(
26068        "7552b3145622: C++ declaration recovery regressed for export class head after object macro lines recovers its name and bases"
26069    )]
26070    fn export_class_head_after_object_macro_lines_recovers_its_name_and_bases() {
26071        // #2924: object-like macro lines before the head demote the `class`
26072        // keyword to a bare identifier and make the macro invocation an
26073        // `init_declarator`, so the head arrives as a `declaration` rather than
26074        // as an ERROR carrying a `class_specifier`. The name is still the last
26075        // identifier before `final`, and `virtual` is a base specifier rather
26076        // than a base.
26077        let source = r#"
26078namespace api {
26079
26080DIAGNOSTIC_PUSH
26081DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE
26082
26083class PROJECT_PUBLIC_API(3, 6) Wrapped final : public virtual api::Outer::Key,
26084                                               public virtual api::Inner::Key {
26085   public:
26086      std::string algo_name() const override;
26087};
26088
26089DIAGNOSTIC_POP
26090
26091}
26092"#;
26093        let parsed = parse_cpp_declarations(source, "object-macro-head.hpp");
26094        let declarations = parsed.declarations();
26095        let class = declarations
26096            .iter()
26097            .find(|unit| unit.is_class() && unit.fq_name() == "api.Wrapped")
26098            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26099        assert_eq!(
26100            parsed.raw_supertypes.get(class),
26101            Some(&vec![
26102                "api::Outer::Key".to_string(),
26103                "api::Inner::Key".to_string()
26104            ]),
26105            "both qualified virtual bases are bases, and `virtual` is not: {declarations:#?}"
26106        );
26107        assert!(
26108            declarations
26109                .iter()
26110                .any(|unit| unit.is_function() && unit.fq_name() == "api.Wrapped.algo_name"),
26111            "the member is owned by the recovered class: {declarations:#?}"
26112        );
26113        assert!(
26114            declarations
26115                .iter()
26116                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26117            "the macro invocation must not mint a declaration: {declarations:#?}"
26118        );
26119    }
26120
26121    fn function_like_export_class_fixture(head: &str, name: &str) -> String {
26122        format!(
26123            r#"
26124namespace api {{
26125class PROJECT_PUBLIC_API(2, 0) Exception : public std::exception {{
26126   public:
26127      /** Return a descriptive string. */
26128      const char* what() const noexcept override {{ return m_msg.c_str(); }}
26129
26130      /** Return the type of error. */
26131      virtual ErrorType error_type() const noexcept {{ return ErrorType::Unknown; }}
26132
26133      /** Return an associated error code. */
26134      virtual int error_code() const noexcept {{ return 0; }}
26135
26136      /** Avoid throwing the base directly. */
26137      explicit Exception(std::string_view msg);
26138
26139      /** Avoid throwing the base directly. */
26140      Exception(const char* prefix, std::string_view msg);
26141
26142      /** Avoid throwing the base directly. */
26143      Exception(std::string_view msg, const std::exception& e);
26144
26145   private:
26146      std::string m_msg;
26147}};
26148
26149class PROJECT_PUBLIC_API(2, 0) {head} : public Exception {{
26150   public:
26151      explicit {name}(std::string_view msg);
26152
26153      explicit {name}(std::string_view msg, std::string_view where);
26154
26155      {name}(std::string_view msg, const std::exception& e);
26156
26157      ErrorType error_type() const noexcept override {{ return ErrorType::InvalidArgument; }}
26158}};
26159}} // namespace api
26160"#
26161        )
26162    }
26163
26164    #[test]
26165    #[hardwon(
26166        "861f46399c96: C++ declaration recovery regressed for embedded function like export class is named by position not spelling"
26167    )]
26168    fn embedded_function_like_export_class_is_named_by_position_not_spelling() {
26169        // The class after a preceding export class follows the same rule
26170        // (#2557): `X509_CA` is the class, `OTHER_MACRO` is decoration. Older
26171        // grammars embedded this head in the preceding class's error; since
26172        // brokk-tree-sitter-cpp 0.24.7 the grammar reads both heads, and the
26173        // class must still be named by position.
26174        for (head, name) in [("X509_CA", "X509_CA"), ("OTHER_MACRO Verdict", "Verdict")] {
26175            let source = function_like_export_class_fixture(head, name);
26176            let tree = crate::grammar_test_support::tree(&source);
26177            crate::grammar_test_support::assert_function_like_export_class_head(
26178                &tree, &source, name,
26179            );
26180
26181            let parsed = parse_cpp_declarations(&source, "embedded-positional-export.hpp");
26182            let explicit = parse_cpp_declarations(
26183                &source
26184                    .replace("PROJECT_PUBLIC_API(2, 0) ", "")
26185                    .replace("OTHER_MACRO ", ""),
26186                "embedded-positional-export.hpp",
26187            );
26188            assert_eq!(
26189                declaration_expansion_inventory(&parsed),
26190                declaration_expansion_inventory(&explicit),
26191                "{head}"
26192            );
26193            let declarations = parsed.declarations();
26194            let class = declarations
26195                .iter()
26196                .find(|unit| unit.is_class() && unit.fq_name() == format!("api.{name}"))
26197                .unwrap_or_else(|| panic!("{head}: missing embedded class: {declarations:#?}"));
26198            assert_eq!(
26199                parsed.raw_supertypes.get(class),
26200                Some(&vec!["Exception".to_string()]),
26201                "{head}"
26202            );
26203            assert!(
26204                declarations
26205                    .iter()
26206                    .all(|unit| unit.identifier() != "OTHER_MACRO"),
26207                "{head}: the object-like macro must not become a declaration: {declarations:#?}"
26208            );
26209        }
26210    }
26211
26212    #[test]
26213    #[hardwon(
26214        "861f46399c96: C++ declaration recovery regressed for function like export class head with virtual qualified bases does not invent a name"
26215    )]
26216    fn function_like_export_class_head_with_virtual_qualified_bases_does_not_invent_a_name() {
26217        // Botan's TPM2 keys (`tpm2_ecc.h`, `tpm2_rsa.h`). The grammar keeps
26218        // `class MACRO(3, 6) Name final :` in the error, the first base as the
26219        // `type` of the following declaration, and closes that declaration with
26220        // a zero-width `MISSING identifier`. Reading the head by position must
26221        // not take that missing node for the class name: an empty name panics
26222        // at `FqName` construction, which took the Botan corpus replay down
26223        // (#2557). The head's tokens straddle the two nodes, so the positional
26224        // read spans both and recovers the class rather than leaving the
26225        // bodyless `class PROJECT_PUBLIC_API` specifier behind (#2924).
26226        let source = r#"
26227namespace api {
26228class PROJECT_PUBLIC_API(3, 6) EC_PublicKey final : public virtual Botan::TPM2::PublicKey,
26229                                                    public virtual Botan::EC_PublicKey {
26230   public:
26231      std::string algo_name() const override { return "ECDSA"; }
26232};
26233} // namespace api
26234"#;
26235        let parsed = parse_cpp_declarations(source, "virtual-qualified-bases.hpp");
26236        let declarations = parsed.declarations();
26237        assert!(
26238            declarations
26239                .iter()
26240                .all(|unit| !unit.identifier().is_empty()),
26241            "no declaration may carry an empty name: {declarations:#?}"
26242        );
26243        assert!(
26244            declarations
26245                .iter()
26246                .all(|unit| !matches!(unit.identifier(), "final" | "public" | "virtual")),
26247            "macros and head specifiers must not become declarations: {declarations:#?}"
26248        );
26249        let class = declarations
26250            .iter()
26251            .find(|unit| unit.is_class() && unit.fq_name() == "api.EC_PublicKey")
26252            .unwrap_or_else(|| panic!("missing recovered class: {declarations:#?}"));
26253        assert_eq!(
26254            parsed.raw_supertypes.get(class),
26255            Some(&vec![
26256                "Botan::TPM2::PublicKey".to_string(),
26257                "Botan::EC_PublicKey".to_string()
26258            ]),
26259            "both qualified virtual bases are bases: {declarations:#?}"
26260        );
26261        let source_facts = parsed.source_facts.as_ref().unwrap();
26262        let declaration = parsed
26263            .source_declaration_units
26264            .iter()
26265            .find(|(_, unit)| unit == class)
26266            .map(|(declaration, _)| *declaration)
26267            .expect("recovered class source bridge");
26268        let bases = source_facts
26269            .cpp
26270            .as_ref()
26271            .unwrap()
26272            .declarations
26273            .iter()
26274            .find(|fact| fact.declaration == declaration)
26275            .expect("recovered class source fact")
26276            .bases
26277            .clone();
26278        assert_eq!(
26279            bases
26280                .iter()
26281                .map(|base| base.components.clone())
26282                .collect::<Vec<_>>(),
26283            vec![
26284                vec!["Botan", "TPM2", "PublicKey"],
26285                vec!["Botan", "EC_PublicKey"],
26286            ],
26287            "generalized recovery must publish structured base components"
26288        );
26289        assert!(
26290            bases.iter().all(|base| base.is_virtual),
26291            "generalized recovery must preserve virtual base qualifiers"
26292        );
26293        assert!(
26294            declarations
26295                .iter()
26296                .all(|unit| unit.identifier() != "PROJECT_PUBLIC_API"),
26297            "the head must not mint a macro-named class: {declarations:#?}"
26298        );
26299        let mixed_source = source.replace(
26300            "public virtual Botan::EC_PublicKey",
26301            "public Botan::EC_PublicKey",
26302        );
26303        let mixed = parse_cpp_declarations(&mixed_source, "mixed-qualified-bases.hpp");
26304        let qualifiers = mixed
26305            .source_facts
26306            .as_ref()
26307            .unwrap()
26308            .cpp
26309            .as_ref()
26310            .unwrap()
26311            .declarations
26312            .iter()
26313            .flat_map(|declaration| &declaration.bases)
26314            .map(|base| base.is_virtual)
26315            .collect::<Vec<_>>();
26316        assert_eq!(
26317            qualifiers,
26318            [true, false],
26319            "base qualifiers must not cross the comma"
26320        );
26321    }
26322
26323    #[test]
26324    #[hardwon(
26325        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26326    )]
26327    fn function_like_export_class_survives_a_preceding_malformed_body() {
26328        // Older grammars left the first class's body malformed and embedded the
26329        // second class in its error. Since brokk-tree-sitter-cpp 0.24.7 both
26330        // heads parse, so both classes are ordinary source declarations.
26331        let source = function_like_export_class_fixture("Invalid_Argument", "Invalid_Argument");
26332        let tree = crate::grammar_test_support::tree(&source);
26333        for name in ["Exception", "Invalid_Argument"] {
26334            crate::grammar_test_support::assert_function_like_export_class_head(
26335                &tree, &source, name,
26336            );
26337        }
26338
26339        let parsed = parse_cpp_file(
26340            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
26341            &source,
26342            &tree,
26343        );
26344        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
26345        let explicit = parse_cpp_file(
26346            &ProjectFile::new(std::env::temp_dir(), "embedded-function-like-export.hpp"),
26347            &expanded,
26348            &crate::grammar_test_support::tree(&expanded),
26349        );
26350        assert_eq!(
26351            declaration_expansion_inventory(&parsed),
26352            declaration_expansion_inventory(&explicit)
26353        );
26354        assert!(
26355            !explicit
26356                .materialization_records
26357                .iter()
26358                .any(|record| matches!(record, MaterializationRecord::RecoveredDeclaration { .. })),
26359            "the explicit expansion needs no recovery: {:#?}",
26360            explicit.materialization_records
26361        );
26362        let declarations = parsed.declarations();
26363        let exception = declarations
26364            .iter()
26365            .find(|unit| unit.is_class() && unit.fq_name() == "api.Exception")
26366            .expect("qualified-base export class");
26367        let invalid = declarations
26368            .iter()
26369            .find(|unit| unit.is_class() && unit.fq_name() == "api.Invalid_Argument")
26370            .expect("class embedded in the preceding malformed body");
26371
26372        assert_eq!(
26373            parsed.raw_supertypes.get(exception),
26374            Some(&vec!["std::exception".to_string()])
26375        );
26376        assert_eq!(
26377            parsed.raw_supertypes.get(invalid),
26378            Some(&vec!["Exception".to_string()])
26379        );
26380        assert!(
26381            !parsed.materialization_records.iter().any(|record| matches!(
26382                record,
26383                MaterializationRecord::RecoveredDeclaration { unit, .. }
26384                    if unit == invalid
26385            )),
26386            "a parsed class must not claim recovery provenance: {:#?}",
26387            parsed.materialization_records
26388        );
26389    }
26390
26391    #[test]
26392    #[hardwon(
26393        "1548bb89de04: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26394    )]
26395    fn function_like_export_class_recovers_a_merged_inline_constructor_shape() {
26396        let source = r#"
26397public:
26398   explicit Lookup_Error(std::string_view err) : Exception(err) {}
26399
26400   Lookup_Error(std::string_view type, std::string_view algo, std::string_view provider = "");
26401"#;
26402        let tree = cpp_reparse_fragmented_class_body(source, 0, source.len())
26403            .expect("reparse merged constructor body");
26404        let (range, body) =
26405            cpp_reparsed_merged_inline_constructor(tree.root_node(), "Lookup_Error", source)
26406                .unwrap_or_else(|| {
26407                    panic!(
26408                        "the merged constructor must retain its structured declarator/body: {}",
26409                        tree.root_node().to_sexp()
26410                    )
26411                });
26412        assert_eq!(
26413            source.get(range).expect("constructor range"),
26414            "Lookup_Error(std::string_view err) : Exception(err) {}"
26415        );
26416        assert_eq!(node_text(body, source), "{}");
26417    }
26418
26419    #[test]
26420    #[hardwon(
26421        "1c570a35813e: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26422    )]
26423    fn cpp_reparsed_members_gate_handles_copy_control_error_only_with_semicolon() {
26424        let positive_source =
26425            "private:\n  virtual ~XMLElement();\n  XMLElement( const XMLElement& )\n  ;\n";
26426        let mut parser = tree_sitter::Parser::new();
26427        parser
26428            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26429            .unwrap();
26430        let positive_tree = parser.parse(positive_source, None).unwrap();
26431        assert!(cpp_reparsed_members_are_indexable(
26432            positive_tree.root_node(),
26433            positive_source
26434        ));
26435
26436        let negative_source = "XMLElement( const XMLElement& )\n++ 0;\n";
26437        let negative_tree = parser.parse(negative_source, None).unwrap();
26438        assert!(!cpp_reparsed_members_are_indexable(
26439            negative_tree.root_node(),
26440            negative_source
26441        ));
26442    }
26443
26444    #[test]
26445    #[hardwon(
26446        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26447    )]
26448    fn cpp_reparsed_members_gate_accepts_cppcheck_copy_control_and_constraint_macros() {
26449        let copy_control_source = r#"
26450public:
26451    Token(const TokenList& tokenlist, std::shared_ptr<State> state);
26452    explicit Token(const Token* tok);
26453    ~Token();
26454    Token* astOperand1() { return nullptr; }
26455"#;
26456        let constraint_source = r#"
26457private:
26458    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26459    static T *tokAtImpl(T *tok, int index) {
26460        return tok;
26461    }
26462
26463    template<class T, REQUIRES("T must be a Token class", std::is_convertible<T*, const Token*> )>
26464    static T *linkAtImpl(T *tok, int index) {
26465        return tok;
26466    }
26467
26468public:
26469    int late() const { return 1; }
26470"#;
26471        let mut parser = tree_sitter::Parser::new();
26472        parser
26473            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26474            .unwrap();
26475        let copy_control_tree = parser
26476            .parse(copy_control_source, None)
26477            .expect("parse copy-control fixture");
26478        assert!(
26479            copy_control_tree.root_node().has_error(),
26480            "fixture must exercise adjacent copy-control recovery"
26481        );
26482        assert!(
26483            cpp_reparsed_members_are_indexable(copy_control_tree.root_node(), copy_control_source),
26484            "a complete late getter must remain recoverable after adjacent copy-control declarations"
26485        );
26486        let mut cursor = copy_control_tree.root_node().walk();
26487        assert!(
26488            copy_control_tree
26489                .root_node()
26490                .named_children(&mut cursor)
26491                .any(|child| cpp_reparsed_adjacent_copy_control_error(child, copy_control_source)),
26492            "fixture must retain the exact explicit-constructor/destructor error geometry: {}",
26493            copy_control_tree.root_node().to_sexp()
26494        );
26495        let constraint_tree = parser
26496            .parse(constraint_source, None)
26497            .expect("parse constraint-macro fixture");
26498        assert!(constraint_tree.root_node().has_error());
26499        assert!(
26500            cpp_reparsed_members_are_indexable(constraint_tree.root_node(), constraint_source),
26501            "complete constraint-macro members must not hide a later ordinary member"
26502        );
26503        let mut cursor = constraint_tree.root_node().walk();
26504        assert!(
26505            constraint_tree
26506                .root_node()
26507                .named_children(&mut cursor)
26508                .any(|child| cpp_reparsed_template_macro_prefix_is_indexable(
26509                    child,
26510                    constraint_source
26511                )),
26512            "fixture must retain the split constraint-macro prefix/function geometry"
26513        );
26514    }
26515
26516    #[test]
26517    #[hardwon(
26518        "ab74ff5a2b2d: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26519    )]
26520    fn fragmented_plain_class_recovers_nested_constrained_constructor_owner() {
26521        let source = r#"
26522struct Analyzer {
26523    struct Action {
26524        Action() = default;
26525        Action(const Action&) = default;
26526        Action& operator=(const Action& rhs) & = default;
26527
26528        template<class T,
26529                 REQUIRES("T must be convertible to unsigned int", std::is_convertible<T, unsigned int> ),
26530                 REQUIRES("T must not be a bool", !std::is_same<T, bool> )>
26531        // NOLINTNEXTLINE(google-explicit-constructor)
26532        Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor
26533        {}
26534
26535        enum : std::uint16_t { None = 0, Read = (1 << 0) };
26536        bool get(unsigned int f) const { return ((mFlag & f) != 0); }
26537
26538    private:
26539        unsigned int mFlag{};
26540    };
26541
26542    enum class Direction : unsigned char { Forward, Reverse };
26543    virtual Action analyze(Direction d) const = 0;
26544};
26545"#;
26546        let mut parser = tree_sitter::Parser::new();
26547        parser
26548            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26549            .unwrap();
26550        let tree = parser.parse(source, None).unwrap();
26551        assert!(tree.root_node().has_error());
26552        let root = tree.root_node();
26553        let outer = root
26554            .named_children(&mut root.walk())
26555            .find(|child| child.kind() == "ERROR")
26556            .expect("fragmented Analyzer prefix");
26557        let outer_recovered =
26558            fragmented_class_body(outer, source).expect("structured Analyzer fragment boundary");
26559        assert_eq!(outer_recovered.name, "Analyzer");
26560        let outer_tree = cpp_reparse_fragmented_class_body(
26561            source,
26562            outer_recovered.body.reparse_start,
26563            outer_recovered.body.reparse_end,
26564        )
26565        .expect("reparse Analyzer body");
26566        let outer_root = outer_tree.root_node();
26567        let action_prefix = outer_root
26568            .named_children(&mut outer_root.walk())
26569            .find(|child| child.kind() == "ERROR")
26570            .expect("fragmented Action prefix");
26571        let action_recovered = fragmented_class_body(action_prefix, source)
26572            .expect("structured Action fragment boundary");
26573        assert_eq!(action_recovered.name, "Action");
26574        let action_tree = cpp_reparse_fragmented_class_body(
26575            source,
26576            action_recovered.body.reparse_start,
26577            action_recovered.body.reparse_end,
26578        )
26579        .expect("reparse Action body");
26580        let action_root = action_tree.root_node();
26581        let macro_prefix = action_root
26582            .named_children(&mut action_root.walk())
26583            .find(|child| child.kind() == "ERROR")
26584            .expect("constraint macro prefix");
26585        let macro_parameter = cpp_reparsed_template_macro_prefix_parameter(macro_prefix, source)
26586            .expect("structured template macro prefix");
26587        let macro_companion =
26588            cpp_next_non_comment_named_sibling(macro_prefix).expect("constraint macro companion");
26589        assert!(
26590            cpp_reparsed_template_macro_constructor_companion_is_indexable(
26591                macro_companion,
26592                macro_parameter,
26593                source,
26594            ),
26595            "split constrained constructor must be admitted: {}",
26596            macro_companion.to_sexp()
26597        );
26598        assert!(
26599            cpp_reparsed_members_are_indexable_for_class(action_root, source, Some("Action")),
26600            "complete Action body must pass the recovery gate: {}",
26601            action_tree.root_node().to_sexp()
26602        );
26603        assert!(
26604            cpp_reparsed_members_are_indexable_for_class(outer_root, source, Some("Analyzer")),
26605            "complete Analyzer body must pass the recovery gate: {}",
26606            outer_tree.root_node().to_sexp()
26607        );
26608        let file = ProjectFile::new(std::env::temp_dir(), "fragmented-analyzer.hpp");
26609        let parsed = parse_cpp_file(&file, source, &tree);
26610        for expected in ["Analyzer", "Analyzer$Action", "Analyzer$Action.get"] {
26611            assert!(
26612                parsed
26613                    .declarations()
26614                    .iter()
26615                    .any(|unit| unit.fq_name() == expected),
26616                "missing recovered declaration {expected}: {:#?}",
26617                parsed.declarations()
26618            );
26619        }
26620        assert!(
26621            parsed
26622                .declarations()
26623                .iter()
26624                .all(|unit| unit.fq_name() != "Action" && unit.fq_name() != "get"),
26625            "nested members must not remain flattened: {:#?}",
26626            parsed.declarations()
26627        );
26628    }
26629
26630    #[test]
26631    #[hardwon("e3ff69209ab2: ABI 15 defaulted-constructor recovery must retain class ownership")]
26632    fn defaulted_constructor_gate_rejects_unrelated_expression_assignments() {
26633        let source = "value() = default;\nint value();\n";
26634        let tree = crate::diagnostics::parse_cpp_tree(source);
26635        let root = tree.root_node();
26636        assert!(
26637            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("Owner")),
26638            "a default assignment and a same-name free declaration do not prove an Owner constructor: {}",
26639            root.to_sexp()
26640        );
26641        assert!(
26642            !cpp_reparsed_members_are_indexable_for_class(root, source, Some("value")),
26643            "a return-typed same-name declaration is not a constructor witness: {}",
26644            root.to_sexp()
26645        );
26646
26647        let damaged_source = r#"
26648value(int x) {
26649  broken(
26650}
26651value() = default;
26652int value();
26653"#;
26654        let damaged_tree = crate::diagnostics::parse_cpp_tree(damaged_source);
26655        let damaged_root = damaged_tree.root_node();
26656        // The macro-declaration grammar absorbs the whole damaged witness
26657        // into one errorful `function_definition`, so the default-assignment
26658        // expression lives inside the bogus body rather than as a top-level
26659        // sibling. The gate's answer must not change: the assignment is not
26660        // the class-named call, and the errorful same-name function is not a
26661        // separate constructor witness.
26662        let mut damaged_stack = vec![damaged_root];
26663        let mut damaged_assignment = None;
26664        while let Some(node) = damaged_stack.pop() {
26665            if node.kind() == "expression_statement" {
26666                damaged_assignment = Some(node);
26667                break;
26668            }
26669            let mut cursor = node.walk();
26670            damaged_stack.extend(node.named_children(&mut cursor));
26671        }
26672        let damaged_assignment =
26673            damaged_assignment.expect("damaged witness probe retains the assignment expression");
26674        let mut damaged_cursor = damaged_root.walk();
26675        let damaged_members = damaged_root
26676            .named_children(&mut damaged_cursor)
26677            .collect::<Vec<_>>();
26678        assert!(
26679            !cpp_reparsed_defaulted_constructor_is_indexable(
26680                damaged_assignment,
26681                &damaged_members,
26682                Some("value"),
26683                damaged_source,
26684            ),
26685            "an errorful same-name function must not witness a recovered defaulted constructor: {}",
26686            damaged_root.to_sexp()
26687        );
26688        assert!(
26689            !cpp_reparsed_members_are_indexable_for_class(
26690                damaged_root,
26691                damaged_source,
26692                Some("value")
26693            ),
26694            "the damaged witness must not pass the member gate either: {}",
26695            damaged_root.to_sexp()
26696        );
26697    }
26698
26699    #[test]
26700    #[hardwon(
26701        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26702    )]
26703    fn cpp_reparsed_members_gate_accepts_complete_errorful_member_functions() {
26704        let source = r#"
26705raw_hash_set& operator=(raw_hash_set&& that) {
26706  return move_assign(
26707      std::move(that),
26708      typename AllocTraits::propagate_on_container_move_assignment());
26709}
26710
26711iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
26712  return {};
26713}
26714
26715void reset() ABSL_ATTRIBUTE_LIFETIME_BOUND {}
26716
26717iterator insert(const_iterator hint, value_type&& value)
26718    ABSL_ATTRIBUTE_LIFETIME_BOUND {
26719  return {};
26720}
26721
26722friend bool operator==(const raw_hash_set& left, const raw_hash_set& right) {
26723  return left.size() == right.size();
26724}
26725
26726static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buffer) {
26727  return static_cast<slot_type*>(buffer);
26728}
26729
26730protected:
26731// Included-range recovery can attach this comment to the template prefix.
26732template <class K>
26733void AssertOnFind([[maybe_unused]] const K& key) {
26734  Check(key);
26735}
26736"#;
26737        let mut parser = tree_sitter::Parser::new();
26738        parser
26739            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26740            .unwrap();
26741        let tree = parser.parse(source, None).unwrap();
26742        assert!(
26743            tree.root_node().has_error(),
26744            "the fixture must exercise tree-sitter's errorful member shapes"
26745        );
26746        assert!(cpp_reparsed_members_are_indexable(tree.root_node(), source));
26747
26748        let incomplete_source = "iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND { return {};\n";
26749        let incomplete_tree = parser.parse(incomplete_source, None).unwrap();
26750        assert!(!cpp_reparsed_members_are_indexable(
26751            incomplete_tree.root_node(),
26752            incomplete_source
26753        ));
26754
26755        let outside_error_source = "int foo() stray_attribute {}\n";
26756        let outside_error_tree = parser.parse(outside_error_source, None).unwrap();
26757        assert!(outside_error_tree.root_node().has_error());
26758        assert!(!cpp_reparsed_members_are_indexable(
26759            outside_error_tree.root_node(),
26760            outside_error_source
26761        ));
26762
26763        let variable_initializer_source = "int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND { bad; }\n";
26764        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26765        assert!(!cpp_reparsed_members_are_indexable(
26766            variable_initializer_tree.root_node(),
26767            variable_initializer_source
26768        ));
26769    }
26770
26771    #[test]
26772    #[hardwon(
26773        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26774    )]
26775    fn cpp_reparsed_members_gate_accepts_paired_attribute_requires_body() {
26776        let positive_source = r#"
26777std::pair<iterator, bool> insert(init_type&& value)
26778    ABSL_ATTRIBUTE_LIFETIME_BOUND
26779#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26780  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26781#endif
26782{
26783  return emplace(std::move(value));
26784}
26785"#;
26786        let mut parser = tree_sitter::Parser::new();
26787        parser
26788            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26789            .unwrap();
26790        let positive_tree = parser.parse(positive_source, None).unwrap();
26791        assert!(
26792            positive_tree.root_node().has_error(),
26793            "the fixture must exercise the split attribute/requires shape"
26794        );
26795        assert!(cpp_reparsed_members_are_indexable(
26796            positive_tree.root_node(),
26797            positive_source
26798        ));
26799
26800        let template_return_source = r#"
26801pair<int> insert(init_type&& value)
26802    ABSL_ATTRIBUTE_LIFETIME_BOUND
26803#if LANGUAGE_LEVEL >= 202002L
26804  requires(!Predicate<init_type>::value)
26805#endif
26806// Attributes and the function body may be separated by comments.
26807{
26808  return {};
26809}
26810"#;
26811        let template_return_tree = parser.parse(template_return_source, None).unwrap();
26812        assert!(
26813            cpp_reparsed_members_are_indexable(
26814                template_return_tree.root_node(),
26815                template_return_source
26816            ),
26817            "template-return attribute/requires tree: {}",
26818            template_return_tree.root_node().to_sexp()
26819        );
26820
26821        let no_body_source = r#"
26822std::pair<iterator, bool> insert(init_type&& value)
26823    ABSL_ATTRIBUTE_LIFETIME_BOUND
26824#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
26825  requires(!IsLifetimeBoundAssignmentFrom<init_type>::value)
26826#endif
26827+ 0;
26828"#;
26829        let no_body_tree = parser.parse(no_body_source, None).unwrap();
26830        assert!(!cpp_reparsed_members_are_indexable(
26831            no_body_tree.root_node(),
26832            no_body_source
26833        ));
26834
26835        let extra_payload_source = r#"
26836pair<int> insert(init_type&& value)
26837    ABSL_ATTRIBUTE_LIFETIME_BOUND
26838#if LANGUAGE_LEVEL >= 202002L
26839  int unrelated;
26840  requires(Predicate<init_type>::value)
26841#endif
26842{
26843  return {};
26844}
26845"#;
26846        let extra_payload_tree = parser.parse(extra_payload_source, None).unwrap();
26847        assert!(!cpp_reparsed_members_are_indexable(
26848            extra_payload_tree.root_node(),
26849            extra_payload_source
26850        ));
26851
26852        let variable_initializer_source = r#"
26853int value(1) ABSL_ATTRIBUTE_LIFETIME_BOUND
26854#if LANGUAGE_LEVEL >= 202002L
26855  requires(true)
26856#endif
26857{
26858  bad;
26859}
26860"#;
26861        let variable_initializer_tree = parser.parse(variable_initializer_source, None).unwrap();
26862        assert!(!cpp_reparsed_members_are_indexable(
26863            variable_initializer_tree.root_node(),
26864            variable_initializer_source
26865        ));
26866    }
26867
26868    #[test]
26869    #[hardwon(
26870        "ef5c28a76765: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
26871    )]
26872    fn sentinel_scope_prefers_deeper_fragmented_class_over_outer_shadow() {
26873        let source = r#"namespace absl {
26874ABSL_NAMESPACE_BEGIN namespace container_internal {
26875
26876class raw_hash_set : public Base {
26877 public:
26878  using value_type = int;
26879
26880  template <class U,
26881            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
26882  void insert(U value) { (void)value; }
26883
26884  struct InsertSlot {
26885    raw_hash_set& s;
26886  };
26887};
26888
26889}
26890ABSL_NAMESPACE_END
26891}"#;
26892        let mut parser = tree_sitter::Parser::new();
26893        parser
26894            .set_language(&tree_sitter_cpp::LANGUAGE.into())
26895            .unwrap();
26896        let tree = parser.parse(source, None).unwrap();
26897        let root = tree.root_node();
26898        let outer_namespace = root
26899            .named_children(&mut root.walk())
26900            .find(|child| child.kind() == "namespace_definition")
26901            .expect("outer absl namespace");
26902        let declaration_list = outer_namespace
26903            .child_by_field_name("body")
26904            .expect("outer namespace body");
26905        let sentinel_function = declaration_list
26906            .named_children(&mut declaration_list.walk())
26907            .find(|child| child.kind() == "function_definition")
26908            .expect("malformed namespace sentinel function");
26909        let ancestry = ParentIndex::new(root);
26910        let sentinel = cpp_nested_namespace_sentinel(sentinel_function, source, &ancestry)
26911            .expect("structured nested namespace sentinel");
26912        let fragmented =
26913            cpp_sentinel_fragmented_class_tail(sentinel.function, sentinel.body, source, &ancestry)
26914                .expect("fragmented raw_hash_set class");
26915        assert_eq!(fragmented.class_node.kind(), "ERROR");
26916        assert_eq!(fragmented.name, "raw_hash_set");
26917        assert_eq!(fragmented.raw_supertypes, Some(vec!["Base".to_string()]));
26918
26919        let outer_scope =
26920            cpp_sentinel_recovered_namespace_components(sentinel.function, &[], source);
26921        let mut outer_siblings = Vec::new();
26922        push_cpp_sentinel_sibling_classes(
26923            &mut outer_siblings,
26924            declaration_list,
26925            sentinel.function,
26926            &outer_scope,
26927            source,
26928            &ancestry,
26929        );
26930        let [outer_shadow] = outer_siblings.as_slice() else {
26931            panic!("expected exactly one apparent outer sibling: {outer_siblings:#?}");
26932        };
26933        assert_eq!(outer_shadow.namespace_scope_components, vec!["absl"]);
26934        assert_eq!(outer_shadow.scope_components, vec!["absl", "InsertSlot"]);
26935
26936        let field = "    raw_hash_set& s;";
26937        let start = source.find(field).expect("InsertSlot field") + 4;
26938        let node = root
26939            .descendant_for_byte_range(start, start + "raw_hash_set".len())
26940            .expect("raw_hash_set type node");
26941        let recovered = cpp_sentinel_recovered_classes(root, source);
26942        let [deep_class] = recovered.as_slice() else {
26943            panic!("outer shadow must be removed in favor of one deep class: {recovered:#?}");
26944        };
26945        assert_eq!(
26946            deep_class.namespace_scope_components,
26947            vec!["absl", "container_internal"]
26948        );
26949        assert_eq!(
26950            deep_class.scope_components,
26951            vec!["absl", "container_internal", "raw_hash_set"]
26952        );
26953        assert!(
26954            deep_class.class_range.start_byte <= outer_shadow.class_range.start_byte
26955                && deep_class.class_range.end_byte >= outer_shadow.class_range.end_byte
26956        );
26957
26958        assert_eq!(
26959            cpp_sentinel_recovered_scope_for_node(node, source, &recovered),
26960            Some(vec![
26961                "absl".to_string(),
26962                "container_internal".to_string(),
26963                "raw_hash_set".to_string(),
26964                "InsertSlot".to_string(),
26965            ])
26966        );
26967
26968        let file = ProjectFile::new(std::env::temp_dir(), "raw-hash-set-sentinel.h");
26969        let parsed = parse_cpp_file(&file, source, &tree);
26970        let raw_hash_set = parsed
26971            .declarations()
26972            .iter()
26973            .find(|unit| unit.is_class() && unit.short_name() == "raw_hash_set")
26974            .expect("recovered raw_hash_set class");
26975        assert_eq!(
26976            raw_hash_set.fq_name(),
26977            "absl::container_internal.raw_hash_set",
26978            "the recovered declaration must publish under the deeper sentinel namespace"
26979        );
26980        assert_eq!(
26981            parsed.raw_supertypes.get(raw_hash_set),
26982            Some(&vec!["Base".to_string()]),
26983            "the structured base clause on the fragmented ERROR prefix must survive publication"
26984        );
26985        assert!(
26986            parsed.materialization_records.iter().any(|record| matches!(
26987                record,
26988                MaterializationRecord::RecoveredDeclaration { recovery, unit }
26989                    if unit == raw_hash_set && *recovery == deep_class.class_range
26990            )),
26991            "the reconstructed class must publish recovered-declaration provenance: {:#?}",
26992            parsed.materialization_records
26993        );
26994    }
26995
26996    /// Issue #2358: recording an aggregate definition must not walk the whole
26997    /// file.
26998    ///
26999    /// `visit_named_class_like_shape` calls `replace_code_unit` for every
27000    /// class-like shape that has a body, so the removal step runs once per
27001    /// aggregate. It used to `retain` over `top_level_declarations` and over
27002    /// *every* child list in the file on each of those calls, comparing whole
27003    /// `CodeUnit`s (which compare their `ProjectFile` first). A generated
27004    /// kernel-type header is nothing but aggregates -- pwru's 2.5MB
27005    /// `vmlinux-x86.h` yields 75,899 declarations -- so the file paid that scan
27006    /// tens of thousands of times over and the C forward differential never
27007    /// finished.
27008    ///
27009    /// A definition the file has not already declared removes nothing, so the
27010    /// honest cost is zero regardless of how many other aggregates surround it.
27011    /// Two sizes an order of magnitude apart pin that the count is not merely
27012    /// small but independent of the file.
27013    ///
27014    /// The declaration walk answers every ancestor question from a
27015    /// [`ParentIndex`] instead of asking tree-sitter, which re-descends from
27016    /// the root for each one (#2361). Substituting the index is only safe
27017    /// because it answers the identical question, so pin that on the shapes
27018    /// this file's recovery paths care about: anonymous and named aggregates,
27019    /// nested namespaces, templates, macro-displaced declarations and the
27020    /// `ERROR` regions a sentinel macro produces. Anonymous nodes are compared
27021    /// too -- `Node::parent` walks the visible tree, not the named one.
27022    #[test]
27023    #[hardwon(
27024        "90181359ed41: C++ declaration recovery regressed for the parent index answers what tree sitter answers"
27025    )]
27026    fn the_parent_index_answers_what_tree_sitter_answers() {
27027        const SHAPES: [&str; 5] = [
27028            "namespace outer { namespace inner { struct Tag { int field; }; } }",
27029            "namespace { static int hidden(); }\nstruct { int anonymous_member; } value;",
27030            "template <typename T>\nclass PROJECT_API Wrapper : public Base<T> {\n  T get() const;\n};",
27031            "#define BEGIN_NS namespace project {\nBEGIN_NS\nclass Widget { void run(); };\n}\n",
27032            "class API Broken : public First, public Second {\n  void member();\n",
27033        ];
27034        for source in SHAPES {
27035            let mut parser = tree_sitter::Parser::new();
27036            parser
27037                .set_language(&tree_sitter_cpp::LANGUAGE.into())
27038                .unwrap();
27039            let tree = parser.parse(source, None).unwrap();
27040            let root = tree.root_node();
27041            let ancestry = ParentIndex::new(root);
27042            let mut nodes = 0usize;
27043            let mut stack = vec![root];
27044            while let Some(node) = stack.pop() {
27045                nodes += 1;
27046                assert_eq!(
27047                    node.parent().map(|parent| parent.id()),
27048                    ancestry.parent(node).map(|parent| parent.id()),
27049                    "the index disagreed with tree-sitter about the parent of {node:?} in {source:?}"
27050                );
27051                let mut cursor = node.walk();
27052                stack.extend(node.children(&mut cursor));
27053            }
27054            assert!(nodes > 1, "{source:?} produced no tree to compare");
27055        }
27056    }
27057
27058    /// Issue #2361: the callable metadata helpers must ask the per-tree parent
27059    /// index for every ancestor edge. Asking tree-sitter directly makes each
27060    /// edge re-descend from the root, turning declaration extraction on a
27061    /// deeply nested generated header from quadratic output work into a cubic
27062    /// tree walk. Exact query counts pin the route without a machine-dependent
27063    /// wall-clock ceiling.
27064    #[test]
27065    #[hardwon(
27066        "e6a524ac8d71: C++ declaration recovery regressed for deeply nested callable ancestor questions use the parent index"
27067    )]
27068    fn deeply_nested_callable_ancestor_questions_use_the_parent_index() {
27069        const DEPTH: usize = 64;
27070        let mut source = String::new();
27071        for level in 0..DEPTH {
27072            writeln!(source, "namespace n{level} {{").unwrap();
27073        }
27074        source.push_str("int deepest(int value);\n");
27075        for _ in 0..DEPTH {
27076            source.push_str("}\n");
27077        }
27078
27079        let mut parser = tree_sitter::Parser::new();
27080        parser
27081            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27082            .unwrap();
27083        let tree = parser.parse(&source, None).unwrap();
27084        let root = tree.root_node();
27085        let ancestry = ParentIndex::new(root);
27086        let mut function_declarator = None;
27087        walk_named_tree_preorder(root, true, |node| {
27088            if node.kind() == "function_declarator" {
27089                function_declarator = Some(node);
27090                WalkControl::Break
27091            } else {
27092                WalkControl::Continue
27093            }
27094        });
27095        let function_declarator = function_declarator.expect("deepest function declarator");
27096        let ancestor_count =
27097            std::iter::successors(function_declarator.parent(), |node| node.parent()).count();
27098
27099        ancestry.reset_parent_query_count_for_test();
27100        let lexical_scope = cpp_callable_lexical_scope(function_declarator, &source, &ancestry);
27101        assert_eq!(DEPTH, lexical_scope.len());
27102        assert_eq!(
27103            ancestor_count + 1,
27104            ancestry.parent_query_count_for_test(),
27105            "lexical-scope ancestry bypassed the parent index"
27106        );
27107
27108        ancestry.reset_parent_query_count_for_test();
27109        assert_eq!(
27110            Some(DispatchExtensibility::Closed),
27111            cpp_callable_dispatch_extensibility(function_declarator, &ancestry)
27112        );
27113        assert_eq!(
27114            ancestor_count,
27115            ancestry.parent_query_count_for_test(),
27116            "dispatch ancestry bypassed the parent index"
27117        );
27118
27119        ancestry.reset_parent_query_count_for_test();
27120        assert_eq!(
27121            CallableLinkage::External,
27122            cpp_callable_linkage(function_declarator, &source, &ancestry)
27123        );
27124        assert_eq!(
27125            ancestor_count + 1,
27126            ancestry.parent_query_count_for_test(),
27127            "linkage ancestry bypassed the parent index"
27128        );
27129
27130        ancestry.reset_parent_query_count_for_test();
27131        assert!(!cpp_callable_is_structural_constructor(
27132            function_declarator,
27133            &source,
27134            &ancestry
27135        ));
27136        assert_eq!(
27137            ancestor_count + 1,
27138            ancestry.parent_query_count_for_test(),
27139            "constructor ancestry bypassed the parent index"
27140        );
27141    }
27142
27143    /// #3507: the one row-level dispatch fact C++ can state is stated only
27144    /// where the language states it -- in the class body.
27145    ///
27146    /// A call's exactness is not readable from a definition row: an
27147    /// out-of-line definition repeats no `virtual`, no `final`, and no base
27148    /// clause, and C++ inherits virtual-ness from a base that declares the
27149    /// member. Each shape below therefore has to answer for itself, and only
27150    /// a class body with no base clause at all can close a member that writes
27151    /// nothing.
27152    #[test]
27153    #[hardwon(
27154        "84b42ca510ce: C++ declaration recovery regressed for callable dispatch extensibility comes from the class body"
27155    )]
27156    fn callable_dispatch_extensibility_comes_from_the_class_body() {
27157        const SOURCE: &str = "\
27158class Plain { public: long send(long order); };
27159class Derived : public Base { public: long send(long order); };
27160class Sealed : public Base { public: long send(long order) final; };
27161class Hiding : public Base { public: long send(long order) { return order; } };
27162class Virtual { public: virtual long send(long order); };
27163namespace shop { long free_call(long order); }
27164long Plain::send(long order) { return order; }
27165";
27166        let mut parser = tree_sitter::Parser::new();
27167        parser
27168            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27169            .unwrap();
27170        let tree = parser.parse(SOURCE, None).unwrap();
27171        let root = tree.root_node();
27172        let ancestry = ParentIndex::new(root);
27173        let mut answers = Vec::new();
27174        walk_named_tree_preorder(root, true, |node| {
27175            if node.kind() != "function_declarator" {
27176                return WalkControl::Continue;
27177            }
27178            let name = cpp_function_declarator_name_node(node).expect("declarator name");
27179            answers.push((
27180                node_text(name, SOURCE).to_string(),
27181                cpp_callable_dispatch_extensibility(node, &ancestry),
27182            ));
27183            WalkControl::Continue
27184        });
27185        assert_eq!(
27186            answers,
27187            vec![
27188                ("send".to_string(), Some(DispatchExtensibility::Closed)),
27189                ("send".to_string(), Some(DispatchExtensibility::Open)),
27190                ("send".to_string(), Some(DispatchExtensibility::Closed)),
27191                ("send".to_string(), Some(DispatchExtensibility::Open)),
27192                ("send".to_string(), Some(DispatchExtensibility::Open)),
27193                ("free_call".to_string(), Some(DispatchExtensibility::Closed)),
27194                ("Plain::send".to_string(), None),
27195            ],
27196            "the class body states the fact; a qualified definition states none"
27197        );
27198    }
27199
27200    #[test]
27201    #[hardwon(
27202        "8b8f8ecaac59: C++ declaration recovery regressed for cpp alias and macro dedup comparison count is linear"
27203    )]
27204    fn cpp_alias_and_macro_dedup_comparison_count_is_linear() {
27205        const DISTINCT_PER_KIND: usize = 64;
27206        let mut source = String::new();
27207        for index in 0..DISTINCT_PER_KIND {
27208            writeln!(source, "typedef int Alias{index};").unwrap();
27209        }
27210        writeln!(source, "typedef long Alias0;").unwrap();
27211        for index in 0..DISTINCT_PER_KIND {
27212            writeln!(source, "#define MACRO_{index} {index}").unwrap();
27213        }
27214        writeln!(source, "#define MACRO_0 duplicate").unwrap();
27215        source.push_str("void overloaded(int value);\nvoid overloaded(double value);\n");
27216
27217        let mut parser = tree_sitter::Parser::new();
27218        parser
27219            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27220            .unwrap();
27221        let tree = parser.parse(&source, None).unwrap();
27222        let file = ProjectFile::new(std::env::temp_dir(), "dedup.cpp");
27223
27224        start_declaration_identity_comparison_probe();
27225        let parsed = parse_cpp_file(&file, &source, &tree);
27226        let comparisons = finish_declaration_identity_comparison_probe();
27227
27228        assert_eq!(
27229            DISTINCT_PER_KIND + 1,
27230            parsed
27231                .declarations()
27232                .iter()
27233                .filter(|unit| unit.is_class() && unit.short_name().starts_with("Alias"))
27234                .count(),
27235            "every physical typedef alias declaration must be retained so \
27236             conditional branch guards stay available to the resolver"
27237        );
27238        assert_eq!(
27239            DISTINCT_PER_KIND + 1,
27240            parsed
27241                .declarations()
27242                .iter()
27243                .filter(|unit| {
27244                    unit.kind() == CodeUnitType::Macro && unit.short_name().starts_with("MACRO_")
27245                })
27246                .count(),
27247            "distinct macro redefinitions must remain available to temporal lookup"
27248        );
27249        assert_eq!(
27250            2,
27251            parsed
27252                .declarations()
27253                .iter()
27254                .filter(|unit| {
27255                    unit.kind() == CodeUnitType::Function && unit.short_name() == "overloaded"
27256                })
27257                .count(),
27258            "function overloads must remain distinct"
27259        );
27260
27261        let dedup_inputs = DISTINCT_PER_KIND * 2 + 2;
27262        assert!(
27263            comparisons <= dedup_inputs * 4,
27264            "semantic-identity dedup should perform O(inputs) comparisons; got {comparisons} comparisons for {dedup_inputs} alias/macro inputs"
27265        );
27266    }
27267
27268    #[test]
27269    #[hardwon("3ba12c745afa: sentinel recovery requires the class's own body close")]
27270    fn sentinel_recovery_admits_errorful_class_with_real_body_close() {
27271        let negative_source = r#"namespace absl {
27272ABSL_NAMESPACE_BEGIN namespace container_internal {
27273template <typename T>
27274class broken {
27275 public:
27276  using value_type = T;
27277  T operator->() const { return &operator*(); }
27278}
27279}
27280"#;
27281        let mut parser = tree_sitter::Parser::new();
27282        parser
27283            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27284            .unwrap();
27285        let negative_tree = parser.parse(negative_source, None).unwrap();
27286        let broken = find_class_named(negative_tree.root_node(), negative_source, "broken")
27287            .expect("the negative fixture must expose the malformed class node");
27288        assert!(
27289            broken.has_error(),
27290            "the negative fixture must retain a parser error"
27291        );
27292        // The macro-declaration grammar keeps the class body closed here; the
27293        // damage is the missing `;` and the unclosed namespace around it, so
27294        // the class's incompleteness is proven by the sentinel recognizers
27295        // finding no envelope for it rather than by a shared parent boundary.
27296        let mut envelope = None;
27297        {
27298            let mut stack = vec![negative_tree.root_node()];
27299            while let Some(node) = stack.pop() {
27300                if cpp_nested_namespace_sentinel(
27301                    node,
27302                    negative_source,
27303                    &ParentIndex::new(negative_tree.root_node()),
27304                )
27305                .is_some()
27306                    || cpp_root_namespace_sentinel(
27307                        node,
27308                        negative_source,
27309                        &ParentIndex::new(negative_tree.root_node()),
27310                    )
27311                    .is_some()
27312                    || cpp_sentinel_macro_class_region(node, negative_source).is_some()
27313                {
27314                    envelope = Some(node);
27315                    break;
27316                }
27317                stack.extend(node.named_children(&mut node.walk()));
27318            }
27319        }
27320        assert!(
27321            envelope.is_none(),
27322            "an incomplete class must not prove a sentinel envelope: {}",
27323            negative_tree.root_node().to_sexp()
27324        );
27325        let recovered = cpp_sentinel_recovered_classes(negative_tree.root_node(), negative_source);
27326        assert!(
27327            recovered
27328                .iter()
27329                .all(|class| class.scope_components != ["absl", "container_internal", "broken"]),
27330            "an incomplete class must not borrow the namespace close: {recovered:#?}"
27331        );
27332
27333        let positive_source = r#"namespace absl {
27334ABSL_NAMESPACE_BEGIN namespace container_internal {
27335template <typename T>
27336class broken {
27337 public:
27338  using value_type = T;
27339  T operator->() const { return &operator*(); }
27340  using alias = value_type;
27341};
27342}
27343}
27344"#;
27345        let positive_tree = parser.parse(positive_source, None).unwrap();
27346        let broken = find_class_named(positive_tree.root_node(), positive_source, "broken")
27347            .expect("the positive fixture must expose the broken class node");
27348        assert!(
27349            broken.has_error(),
27350            "the positive fixture must retain an internal parser error"
27351        );
27352        assert!(
27353            cpp_complete_class_body_close(broken).is_some(),
27354            "the positive fixture must expose a real class body close"
27355        );
27356        let recovered = cpp_sentinel_recovered_classes(positive_tree.root_node(), positive_source);
27357        assert!(
27358            recovered.iter().any(|class| {
27359                class.scope_components == ["absl", "container_internal", "broken"]
27360            }),
27361            "a complete class body must be recovered despite an internal parser error: {recovered:#?}"
27362        );
27363    }
27364
27365    #[test]
27366    #[hardwon(
27367        "0e6df36dfa5d: C++ declaration recovery regressed for sentinel recovery keeps members after nested body close"
27368    )]
27369    fn sentinel_recovery_keeps_members_after_nested_body_close() {
27370        let source = r#"NLOHMANN_JSON_NAMESPACE_BEGIN
27371NLOHMANN_BASIC_JSON_TPL_DECLARATION
27372class basic_json {
27373 private:
27374  union storage {
27375    int value;
27376  } data;
27377 public:
27378  using late_alias = int;
27379  late_alias value() const;
27380};
27381NLOHMANN_JSON_NAMESPACE_END
27382"#;
27383        let mut parser = tree_sitter::Parser::new();
27384        parser
27385            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27386            .unwrap();
27387        let tree = parser.parse(source, None).unwrap();
27388        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27389        let basic_json = recovered
27390            .iter()
27391            .find(|class| {
27392                class
27393                    .scope_components
27394                    .last()
27395                    .is_some_and(|name| name == "basic_json")
27396            })
27397            .unwrap_or_else(|| panic!("the fragmented class must be recovered: {recovered:#?}"));
27398        let late_alias = source
27399            .find("late_alias value")
27400            .expect("late alias reference");
27401        assert!(
27402            basic_json.class_range.start_byte < late_alias
27403                && late_alias < basic_json.class_range.end_byte,
27404            "the recovered class range must include members after a nested close: {basic_json:#?}"
27405        );
27406    }
27407
27408    #[test]
27409    #[hardwon(
27410        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery collects guarded sibling owner without crossing namespace sibling"
27411    )]
27412    fn sentinel_recovery_collects_guarded_sibling_owner_without_crossing_namespace_sibling() {
27413        let source = r#"namespace absl {
27414ABSL_NAMESPACE_BEGIN namespace container_internal {
27415template <typename T>
27416struct broken {
27417  using value_type = T;
27418};
27419}
27420
27421#ifdef OWNER_DEF
27422template <typename T>
27423typename broken<T>::value_type broken<T>::method() {
27424  value_type value{};
27425  return value;
27426}
27427#endif
27428
27429namespace sibling {
27430template <typename T>
27431typename broken<T>::value_type broken<T>::other() {
27432  value_type value{};
27433  return value;
27434}
27435}
27436
27437ABSL_NAMESPACE_END
27438}
27439"#;
27440        let mut parser = tree_sitter::Parser::new();
27441        parser
27442            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27443            .unwrap();
27444        let tree = parser.parse(source, None).unwrap();
27445        let recovered = cpp_sentinel_recovered_classes(tree.root_node(), source);
27446        let broken = recovered
27447            .iter()
27448            .find(|class| class.scope_components == ["absl", "container_internal", "broken"])
27449            .expect("the sentinel class must be recovered");
27450        let method_start = source
27451            .find("typename broken<T>::value_type broken<T>::method()")
27452            .expect("guarded sibling owner");
27453        let method_end = source[method_start..]
27454            .find("\n}")
27455            .map(|offset| method_start + offset + 2)
27456            .expect("guarded sibling owner close");
27457        assert!(
27458            broken
27459                .owner_ranges
27460                .iter()
27461                .any(|owner| owner.range.start_byte <= method_start
27462                    && method_end <= owner.range.end_byte),
27463            "guarded sibling owner must be attached to the recovered class: {broken:#?}"
27464        );
27465        let sibling_start = source
27466            .find("typename broken<T>::value_type broken<T>::other()")
27467            .expect("nested namespace sibling owner");
27468        assert!(
27469            broken
27470                .owner_ranges
27471                .iter()
27472                .all(|owner| owner.range.start_byte > sibling_start
27473                    || owner.range.end_byte <= sibling_start),
27474            "a parser-visible namespace sibling must not inherit the recovered class scope: {broken:#?}"
27475        );
27476    }
27477
27478    #[test]
27479    #[hardwon(
27480        "06f7e51d8ae6: C++ declaration recovery regressed for sentinel recovery discards outer siblings without namespace end marker"
27481    )]
27482    fn sentinel_recovery_discards_outer_siblings_without_namespace_end_marker() {
27483        let source = r#"#ifdef OUTER
27484namespace absl {
27485ABSL_NAMESPACE_BEGIN namespace container_internal {
27486template <typename T>
27487struct broken {
27488  using value_type = T;
27489};
27490}
27491}
27492
27493#ifdef OWNER_DEF
27494template <typename T>
27495typename broken<T>::value_type broken<T>::method() {
27496  value_type value{};
27497  return value;
27498}
27499#endif
27500#endif
27501"#;
27502        // The macro-declaration grammar keeps the sentinel envelope clean, so
27503        // the recovered-region index has nothing malformed to repair; the
27504        // declaration walk owns the recovery, and the out-of-line definition
27505        // after the missing ABSL_NAMESPACE_END must stay unowned.
27506        let parsed = parse_cpp_declarations(source, "discard-outer.h");
27507        assert!(
27508            parsed
27509                .declarations()
27510                .iter()
27511                .any(|unit| unit.is_class() && unit.fq_name() == "absl::container_internal.broken"),
27512            "the sentinel class must be recovered: {:#?}",
27513            parsed.declarations()
27514        );
27515        let method = parsed
27516            .declarations()
27517            .iter()
27518            .find(|unit| unit.is_function() && unit.short_name().ends_with("method"))
27519            .expect("the guarded out-of-line definition");
27520        assert_eq!(
27521            method.fq_name(),
27522            "broken.method",
27523            "missing ABSL_NAMESPACE_END must not attach outer sibling owners: {:#?}",
27524            parsed.declarations()
27525        );
27526    }
27527
27528    /// Every identity signature emitted for `fq_name`, deduplicated, sorted.
27529    fn identity_signatures(parsed: &ParsedFile, fq_name: &str) -> Vec<String> {
27530        let mut signatures = all_function_identities(parsed)
27531            .into_iter()
27532            .filter(|(name, _)| name == fq_name)
27533            .filter_map(|(_, signature)| signature)
27534            .collect::<Vec<_>>();
27535        signatures.sort();
27536        signatures.dedup();
27537        signatures
27538    }
27539
27540    #[test]
27541    #[hardwon(
27542        "64ce2f1b00cf: C++ declaration recovery regressed for callable parameter types come from the ast parameter list"
27543    )]
27544    fn callable_parameter_types_come_from_the_ast_parameter_list() {
27545        let source = r#"
27546template <typename T, ENABLE_BYTES(T)>
27547Vec256<T> DupOdd(Vec256<T> value) { return value; }
27548
27549struct Visitor {
27550  void fail(this auto const& self) {}
27551  void consume(this Visitor const& self, int count) {}
27552};
27553"#;
27554        let parsed = parse_cpp_declarations(source, "structured-parameter-types.cpp");
27555        let dup_odd = parsed
27556            .declarations()
27557            .iter()
27558            .find(|unit| unit.is_function() && unit.fq_name() == "DupOdd")
27559            .expect("DupOdd declaration");
27560        assert_eq!(
27561            dup_odd.signature(),
27562            Some("<typename T, ENABLE_BYTES(T)>(Vec256<T>)")
27563        );
27564        assert_eq!(
27565            parsed
27566                .signature_metadata
27567                .get(dup_odd)
27568                .and_then(|metadata| metadata.first())
27569                .and_then(SignatureMetadata::callable_parameter_types),
27570            Some(["Vec256<T>".to_string()].as_slice())
27571        );
27572
27573        let fail = parsed
27574            .declarations()
27575            .iter()
27576            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.fail")
27577            .expect("explicit-object member");
27578        assert_eq!(fail.signature(), Some("(this const auto &)"));
27579        let metadata = parsed
27580            .signature_metadata
27581            .get(fail)
27582            .and_then(|metadata| metadata.first())
27583            .expect("explicit-object signature metadata");
27584        assert_eq!(metadata.callable_parameter_types(), Some([].as_slice()));
27585        assert!(
27586            metadata
27587                .callable_arity()
27588                .is_some_and(|arity| arity.accepts(0))
27589        );
27590        let consume = parsed
27591            .declarations()
27592            .iter()
27593            .find(|unit| unit.is_function() && unit.fq_name() == "Visitor.consume")
27594            .expect("explicit-object member with an invocation argument");
27595        assert_eq!(consume.signature(), Some("(this const Visitor &, int)"));
27596        let metadata = parsed
27597            .signature_metadata
27598            .get(consume)
27599            .and_then(|metadata| metadata.first())
27600            .expect("typed explicit-object signature metadata");
27601        assert_eq!(
27602            metadata.callable_parameter_types(),
27603            Some(["int".to_string()].as_slice())
27604        );
27605        assert!(
27606            metadata
27607                .callable_arity()
27608                .is_some_and(|arity| arity.accepts(1) && !arity.accepts(0) && !arity.accepts(2))
27609        );
27610    }
27611
27612    #[test]
27613    #[hardwon(
27614        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers survive parameter list whitespace"
27615    )]
27616    fn trailing_qualifiers_survive_parameter_list_whitespace() {
27617        // #1827: the trailing `const`/`noexcept`/ref-qualifier belongs to the
27618        // declarator's structure, so an out-of-line definition that spells its
27619        // parameter list with different whitespace than the declaration must
27620        // still carry it.
27621        let source = r#"
27622struct Widget {
27623  bool multiline(int settings, int supprs) const;
27624  bool doublespace(int settings, int supprs) const;
27625  bool noexcept_multiline(int settings, int supprs) noexcept;
27626  bool ref_multiline(int settings, int supprs) &&;
27627};
27628bool
27629Widget::multiline (int settings,
27630                   int supprs) const
27631{ return settings + supprs > 0; }
27632bool Widget::doublespace(int settings,  int supprs) const { return true; }
27633bool Widget::noexcept_multiline(int settings,
27634                                int supprs) noexcept { return true; }
27635bool Widget::ref_multiline(int settings,
27636                           int supprs) && { return true; }
27637"#;
27638        let parsed = parse_cpp_declarations(source, "trailing-qualifiers.cpp");
27639        assert_eq!(
27640            vec!["(int, int) const".to_string()],
27641            identity_signatures(&parsed, "Widget.multiline")
27642        );
27643        assert_eq!(
27644            vec!["(int, int) const".to_string()],
27645            identity_signatures(&parsed, "Widget.doublespace")
27646        );
27647        assert_eq!(
27648            vec!["(int, int) noexcept".to_string()],
27649            identity_signatures(&parsed, "Widget.noexcept_multiline")
27650        );
27651        assert_eq!(
27652            vec!["(int, int) &&".to_string()],
27653            identity_signatures(&parsed, "Widget.ref_multiline")
27654        );
27655    }
27656
27657    #[test]
27658    #[hardwon(
27659        "3a22f72d49b5: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
27660    )]
27661    fn macro_fragmented_plain_class_keeps_following_member_signature() {
27662        let source = r#"
27663struct CString {};
27664class CMessage {
27665public:
27666  CString GetParams(unsigned int index, unsigned int length = -1) const
27667      ZNC_MSG_DEPRECATED("Use GetParamsColon() instead") {
27668    return GetParamsColon(index, length);
27669  }
27670  CString GetParamsColon(unsigned int index, unsigned int length = -1) const;
27671};
27672CString CMessage::GetParamsColon(unsigned int index, unsigned int length) const {
27673  return {};
27674}
27675"#;
27676        let parsed = parse_cpp_declarations(source, "macro-fragmented-signature.cpp");
27677        assert_eq!(
27678            vec!["(unsigned int, unsigned int) const".to_string()],
27679            identity_signatures(&parsed, "CMessage.GetParamsColon")
27680        );
27681    }
27682
27683    #[test]
27684    #[hardwon(
27685        "ed735e56af02: C++ declaration recovery regressed for namespaced macro fragment keeps prefix members and following classes"
27686    )]
27687    fn namespaced_macro_fragment_keeps_prefix_members_and_following_classes() {
27688        let source = r#"
27689#pragma once
27690#define DEMO_DEPRECATED(message)
27691namespace demo {
27692struct Base {
27693    static int aligned(int value) { return value; }
27694    int legacy(int value) const
27695        DEMO_DEPRECATED("use replacement()") { return value; }
27696    int replacement() const;
27697    void run(int value);
27698};
27699struct OtherBase {
27700    void run(int value);
27701    static int aligned(int value) { return value; }
27702};
27703struct Derived : Base {};
27704struct Override : Base {
27705    void run(int value);
27706    static int aligned(int value) { return value; }
27707};
27708struct RecoveredOverride : Base {
27709    int legacy(int value) const
27710        DEMO_DEPRECATED("use replacement()") { return value; }
27711    void run(int value);
27712};
27713struct Hidden : Base {
27714    void run(int first, int second);
27715    static int aligned(int first, int second) { return first + second; }
27716};
27717struct Ambiguous : Base, OtherBase {};
27718}
27719struct Global {};
27720"#;
27721        let parsed = parse_cpp_declarations(source, "namespaced-macro-fragment.cpp");
27722        let declarations = parsed.declarations();
27723        let fq_names = declarations
27724            .iter()
27725            .map(|unit| unit.fq_name())
27726            .collect::<std::collections::BTreeSet<_>>();
27727
27728        for expected in [
27729            "demo.Base",
27730            "demo.Base.aligned",
27731            "demo.Base.legacy",
27732            "demo.Base.replacement",
27733            "demo.Base.run",
27734            "demo.Derived",
27735            "demo.OtherBase",
27736            "demo.Override",
27737            "demo.RecoveredOverride",
27738            "demo.Hidden",
27739            "demo.Ambiguous",
27740            "Global",
27741        ] {
27742            assert!(
27743                fq_names.contains(expected),
27744                "missing {expected} from namespaced macro fragment: {declarations:#?}"
27745            );
27746        }
27747        assert!(
27748            !fq_names.contains("Derived"),
27749            "following class escaped its namespace: {declarations:#?}"
27750        );
27751        assert!(
27752            !fq_names.contains("demo.Global"),
27753            "global class crossed the recovered namespace boundary: {declarations:#?}"
27754        );
27755    }
27756
27757    #[test]
27758    #[hardwon(
27759        "026332d8e7db: C++ declaration recovery regressed for trailing qualifiers still separate genuine overloads"
27760    )]
27761    fn trailing_qualifiers_still_separate_genuine_overloads() {
27762        // The qualifier must keep distinguishing the real C++ overload sets it
27763        // exists for: a const and a non-const accessor, and a `&`/`&&` pair.
27764        let source = r#"
27765struct Widget {
27766  int* slot(int index);
27767  const int* slot(int index) const;
27768  int log(int severity) &;
27769  int log(int severity) &&;
27770};
27771"#;
27772        let parsed = parse_cpp_declarations(source, "qualifier-overloads.cpp");
27773        assert_eq!(
27774            vec!["(int)".to_string(), "(int) const".to_string()],
27775            identity_signatures(&parsed, "Widget.slot")
27776        );
27777        assert_eq!(
27778            vec!["(int) &".to_string(), "(int) &&".to_string()],
27779            identity_signatures(&parsed, "Widget.log")
27780        );
27781    }
27782
27783    #[test]
27784    #[hardwon(
27785        "026332d8e7db: C++ declaration recovery regressed for virtual specifier is not part of the identity signature"
27786    )]
27787    fn virtual_specifier_is_not_part_of_the_identity_signature() {
27788        // `override` never appears on the out-of-line definition, and C++ does
27789        // not make it part of the signature, so it must not split the identity.
27790        let source = r#"
27791struct Base {
27792  virtual void run(int value) const;
27793};
27794struct Widget : Base {
27795  void run(int value) const override;
27796};
27797void Widget::run(int value) const {}
27798"#;
27799        let parsed = parse_cpp_declarations(source, "virtual-specifier.cpp");
27800        assert_eq!(
27801            vec!["(int) const".to_string()],
27802            identity_signatures(&parsed, "Widget.run")
27803        );
27804    }
27805
27806    #[test]
27807    #[hardwon("026332d8e7db: top-level cv is ignored without collapsing pointee overloads")]
27808    fn top_level_parameter_cv_qualifiers_do_not_split_identity() {
27809        // [dcl.fct]/5: top-level cv-qualifiers on a parameter are not part of
27810        // the function type, so a declaration that spells `const int` and a
27811        // definition that spells `int` are one entity.
27812        let source = r#"
27813struct Widget {
27814  bool value_params(const int settings, const int supprs);
27815  void pointee_const(const int* p);
27816  void pointer_const(int* const p);
27817  void both_const(const int* const p);
27818  void reference_const(const int& p);
27819  void array_const(const int values[4]);
27820  void take(const int* p);
27821  void take(int* p);
27822};
27823bool Widget::value_params(int settings, int supprs) { return true; }
27824void Widget::pointer_const(int* p) {}
27825void Widget::both_const(const int* p) {}
27826"#;
27827        let parsed = parse_cpp_declarations(source, "top-level-const.cpp");
27828        assert_eq!(
27829            vec!["(int, int)".to_string()],
27830            identity_signatures(&parsed, "Widget.value_params")
27831        );
27832        assert_eq!(
27833            vec!["(int *)".to_string()],
27834            identity_signatures(&parsed, "Widget.pointer_const")
27835        );
27836        assert_eq!(
27837            vec!["(const int *)".to_string()],
27838            identity_signatures(&parsed, "Widget.both_const")
27839        );
27840        // The const that is not top-level still distinguishes the type.
27841        assert_eq!(
27842            vec!["(const int *)".to_string()],
27843            identity_signatures(&parsed, "Widget.pointee_const")
27844        );
27845        assert_eq!(
27846            vec!["(const int &)".to_string()],
27847            identity_signatures(&parsed, "Widget.reference_const")
27848        );
27849        assert_eq!(
27850            vec!["(const int [4])".to_string()],
27851            identity_signatures(&parsed, "Widget.array_const")
27852        );
27853        assert_eq!(
27854            vec!["(const int *)".to_string(), "(int *)".to_string()],
27855            identity_signatures(&parsed, "Widget.take")
27856        );
27857    }
27858
27859    fn comparable_shapes(source: &str, callable_name: &str) -> Vec<CppComparableSlot> {
27860        let mut parser = tree_sitter::Parser::new();
27861        parser
27862            .set_language(&tree_sitter_cpp::LANGUAGE.into())
27863            .unwrap();
27864        let tree = parser.parse(source, None).unwrap();
27865        let start = source.find(callable_name).expect("callable declaration");
27866        let declarator =
27867            cpp_function_declarator_at(tree.root_node(), start).expect("function declarator");
27868        cpp_comparable_parameter_shapes(declarator, source, &ParentIndex::unindexed())
27869            .expect("valid fixture parameter roles")
27870    }
27871
27872    fn sole_comparable_shape(source: &str, callable_name: &str) -> CppComparableParameter {
27873        let mut shapes = comparable_shapes(source, callable_name);
27874        assert_eq!(1, shapes.len(), "{shapes:?}");
27875        match shapes.remove(0) {
27876            CppComparableSlot::Shape(shape) => shape,
27877            other => panic!("expected a comparable shape, got {other:?}"),
27878        }
27879    }
27880
27881    fn comparable_named_leaf(shape: &CppComparableParameter) -> &CppComparableNode {
27882        let mut current = shape.root();
27883        loop {
27884            match shape.node(current) {
27885                CppComparableNode::Named { .. } => return shape.node(current),
27886                CppComparableNode::Pointer { inner, .. }
27887                | CppComparableNode::Reference { inner }
27888                | CppComparableNode::Array { inner } => current = *inner,
27889                CppComparableNode::Generic { base, .. } => current = *base,
27890            }
27891        }
27892    }
27893
27894    #[test]
27895    #[hardwon(
27896        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps pointee const"
27897    )]
27898    fn comparable_shape_keeps_pointee_const() {
27899        assert_ne!(
27900            sole_comparable_shape("void f(const char* p);", "f("),
27901            sole_comparable_shape("void f(char* p);", "f(")
27902        );
27903    }
27904
27905    #[test]
27906    #[hardwon(
27907        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps inner pointer const"
27908    )]
27909    fn comparable_shape_keeps_inner_pointer_const() {
27910        assert_ne!(
27911            sole_comparable_shape("void f(int** p);", "f("),
27912            sole_comparable_shape("void f(int* const* p);", "f(")
27913        );
27914    }
27915
27916    #[test]
27917    #[hardwon(
27918        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level pointer const"
27919    )]
27920    fn comparable_shape_drops_top_level_pointer_const() {
27921        assert_eq!(
27922            sole_comparable_shape("void f(int* const p);", "f("),
27923            sole_comparable_shape("void f(int* p);", "f(")
27924        );
27925    }
27926
27927    #[test]
27928    #[hardwon(
27929        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape drops top level base const"
27930    )]
27931    fn comparable_shape_drops_top_level_base_const() {
27932        assert_eq!(
27933            sole_comparable_shape("void f(const int p);", "f("),
27934            sole_comparable_shape("void f(int p);", "f(")
27935        );
27936    }
27937
27938    #[test]
27939    #[hardwon(
27940        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape decays top level array to pointer"
27941    )]
27942    fn comparable_shape_decays_top_level_array_to_pointer() {
27943        assert_eq!(
27944            sole_comparable_shape("void f(int a[3]);", "f("),
27945            sole_comparable_shape("void f(int* a);", "f(")
27946        );
27947        assert_eq!(
27948            sole_comparable_shape("void f(int* a[3]);", "f("),
27949            sole_comparable_shape("void f(int** a);", "f(")
27950        );
27951    }
27952
27953    #[test]
27954    #[hardwon(
27955        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps array behind pointer"
27956    )]
27957    fn comparable_shape_keeps_array_behind_pointer() {
27958        assert_ne!(
27959            sole_comparable_shape("struct S { void f(int (*a)[3]); };", "f("),
27960            sole_comparable_shape("struct S { void f(int** a); };", "f(")
27961        );
27962    }
27963
27964    #[test]
27965    #[hardwon(
27966        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape records written name and lexical scope"
27967    )]
27968    fn comparable_shape_records_written_name_and_lexical_scope() {
27969        let declared =
27970            sole_comparable_shape("namespace ns { struct S { void g(Msg* m); }; }", "g(");
27971        let defined = sole_comparable_shape("void ns::S::g(ns::Msg* m) {}", "g(");
27972        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&declared) else {
27973            panic!("named leaf");
27974        };
27975        assert_eq!(["Msg".to_string()].as_slice(), name.path());
27976        assert_eq!(
27977            ["ns".to_string(), "S".to_string()].as_slice(),
27978            name.lexical_scope()
27979        );
27980        let CppComparableNode::Named { name, .. } = comparable_named_leaf(&defined) else {
27981            panic!("named leaf");
27982        };
27983        assert_eq!(
27984            ["ns".to_string(), "Msg".to_string()].as_slice(),
27985            name.path()
27986        );
27987        assert!(name.lexical_scope().is_empty());
27988        assert_ne!(declared, defined);
27989    }
27990
27991    #[test]
27992    #[hardwon(
27993        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape marks sized primitive leaf"
27994    )]
27995    fn comparable_shape_marks_sized_primitive_leaf() {
27996        let shape = sole_comparable_shape("void f(unsigned char c);", "f(");
27997        let CppComparableNode::Named {
27998            name, primitive, ..
27999        } = comparable_named_leaf(&shape)
28000        else {
28001            panic!("named leaf");
28002        };
28003        assert!(primitive);
28004        assert_eq!(["unsigned char".to_string()].as_slice(), name.path());
28005        assert_ne!(shape, sole_comparable_shape("void f(char c);", "f("));
28006    }
28007
28008    #[test]
28009    #[hardwon(
28010        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports function pointer parameter as unstructured"
28011    )]
28012    fn comparable_shape_reports_function_pointer_parameter_as_unstructured() {
28013        assert_eq!(
28014            vec![CppComparableSlot::Unstructured],
28015            comparable_shapes("void f(void (*cb)(int));", "f(")
28016        );
28017    }
28018
28019    #[test]
28020    #[hardwon(
28021        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape reports ellipsis slot"
28022    )]
28023    fn comparable_shape_reports_ellipsis_slot() {
28024        let shapes = comparable_shapes("void f(int a, ...);", "f(");
28025        assert_eq!(2, shapes.len(), "{shapes:?}");
28026        assert_eq!(CppComparableSlot::Ellipsis, shapes[1]);
28027    }
28028
28029    #[test]
28030    #[hardwon(
28031        "17ad29c5a08c: C++ declaration recovery regressed for comparable shape keeps template argument const"
28032    )]
28033    fn comparable_shape_keeps_template_argument_const() {
28034        assert_ne!(
28035            sole_comparable_shape("void f(std::vector<const int*> v);", "f("),
28036            sole_comparable_shape("void f(std::vector<int*> v);", "f(")
28037        );
28038    }
28039
28040    /// The issue #1970 fixture: C has no nested tag scope, so `inner` is a
28041    /// file-scope tag that a later `struct inner *` at file scope may name.
28042    #[test]
28043    #[hardwon(
28044        "9cce90a89128: C++ declaration recovery regressed for c file mints aggregate member tag at file scope"
28045    )]
28046    fn c_file_mints_aggregate_member_tag_at_file_scope() {
28047        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
28048        let parsed = parse_cpp_declarations(source, "x.c");
28049        let declarations = parsed.declarations();
28050
28051        assert!(
28052            declarations
28053                .iter()
28054                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
28055            "expected a file-scope inner tag, got {declarations:?}"
28056        );
28057        assert!(
28058            declarations
28059                .iter()
28060                .all(|unit| unit.fq_name() != "outer$inner"),
28061            "expected no nested identity, got {declarations:?}"
28062        );
28063        assert!(
28064            declarations
28065                .iter()
28066                .any(|unit| unit.is_class() && unit.fq_name() == "outer")
28067        );
28068        // Members still belong to their own aggregate.
28069        assert!(
28070            declarations
28071                .iter()
28072                .any(|unit| unit.fq_name() == "inner.value")
28073        );
28074        assert!(
28075            declarations
28076                .iter()
28077                .any(|unit| unit.fq_name() == "outer.item")
28078        );
28079
28080        let outer = declarations
28081            .iter()
28082            .find(|unit| unit.is_class() && unit.fq_name() == "outer")
28083            .expect("outer");
28084        assert!(
28085            parsed
28086                .children
28087                .get(outer)
28088                .into_iter()
28089                .flatten()
28090                .all(|child| child.fq_name() != "inner"),
28091            "the tag must not hang off the aggregate it is written inside: {:?}",
28092            parsed.children
28093        );
28094    }
28095
28096    /// A header carries no compilation language of its own, and a `.cpp`
28097    /// translation unit really does declare a nested class. Both keep exactly
28098    /// the C++ extraction they had before the C dialect existed.
28099    #[test]
28100    #[hardwon(
28101        "9cce90a89128: C++ declaration recovery regressed for header and cpp files keep nested tag identity"
28102    )]
28103    fn header_and_cpp_files_keep_nested_tag_identity() {
28104        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
28105        for name in ["x.h", "x.cpp", "x.cc", "x.cxx"] {
28106            let parsed = parse_cpp_declarations(source, name);
28107            let declarations = parsed.declarations();
28108            assert!(
28109                declarations
28110                    .iter()
28111                    .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
28112                "{name} must keep the nested identity, got {declarations:?}"
28113            );
28114            assert!(
28115                declarations.iter().all(|unit| unit.fq_name() != "inner"),
28116                "{name} must not mint a file-scope tag, got {declarations:?}"
28117            );
28118            assert!(
28119                declarations
28120                    .iter()
28121                    .any(|unit| unit.fq_name() == "outer$inner.value")
28122            );
28123        }
28124    }
28125
28126    #[test]
28127    fn c_projection_keeps_defaulted_constructor_nested_in_template_class() {
28128        let source = r#"
28129template <class K, class V, class Traits = CacheTraits<K>>
28130class ARCCache {
28131 public:
28132    struct ArcSizeInfo {
28133        void BSize() {}
28134    };
28135 private:
28136    struct BMapVal;
28137    struct BListVal;
28138    typedef std::unordered_map<K, BMapVal> BMap;
28139    typedef typename BMap::iterator bmap_iter;
28140    typedef typename std::list<BListVal> BList;
28141    typedef typename BList::iterator blist_iter;
28142    struct BListVal {
28143        bmap_iter map_iter;
28144        BListVal() = default;
28145    };
28146    struct DefinedVal {
28147        DefinedVal() {}
28148    };
28149};
28150"#;
28151        let tree = crate::diagnostics::parse_cpp_tree(source);
28152        let file = ProjectFile::new(std::env::temp_dir(), "arc-cache.hpp");
28153        let cpp = crate::adapter::parse_cpp_file_in_dialect(
28154            &file,
28155            source,
28156            &tree,
28157            LanguageDialect::Standard(Language::Cpp),
28158        );
28159        let c =
28160            crate::adapter::parse_cpp_file_in_dialect(&file, source, &tree, LanguageDialect::CppC);
28161        for (dialect, parsed) in [("C++", &cpp), ("C", &c)] {
28162            for fq_name in [
28163                "ARCCache$ArcSizeInfo.BSize",
28164                "ARCCache$BListVal.BListVal",
28165                "ARCCache$DefinedVal.DefinedVal",
28166            ] {
28167                assert!(
28168                    parsed
28169                        .declarations()
28170                        .iter()
28171                        .any(|unit| unit.fq_name() == fq_name),
28172                    "{dialect} reading must contain {fq_name}: {:#?}",
28173                    parsed.declarations()
28174                );
28175            }
28176            for flattened in [
28177                "ArcSizeInfo.BSize",
28178                "BListVal.BListVal",
28179                "DefinedVal.DefinedVal",
28180            ] {
28181                assert!(
28182                    parsed
28183                        .declarations()
28184                        .iter()
28185                        .all(|unit| unit.fq_name() != flattened),
28186                    "{dialect} reading must not flatten {flattened}: {:#?}",
28187                    parsed.declarations()
28188                );
28189            }
28190        }
28191    }
28192
28193    /// Uppercase `.C` conventionally means C++, so it keeps C++ scoping.
28194    #[test]
28195    #[hardwon(
28196        "9cce90a89128: C++ declaration recovery regressed for uppercase c extension keeps cpp tag scope"
28197    )]
28198    fn uppercase_c_extension_keeps_cpp_tag_scope() {
28199        let source = "struct outer {\n  struct inner { int value; } item;\n};\n";
28200        let parsed = parse_cpp_declarations(source, "x.C");
28201        assert!(
28202            parsed
28203                .declarations()
28204                .iter()
28205                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner")
28206        );
28207    }
28208
28209    /// There is no such thing as a partially nested tag in C: every level of a
28210    /// nested aggregate chain lands at the same enclosing scope.
28211    #[test]
28212    #[hardwon(
28213        "9cce90a89128: C++ declaration recovery regressed for c file mints every nesting level at file scope"
28214    )]
28215    fn c_file_mints_every_nesting_level_at_file_scope() {
28216        let source = "struct a { struct b { struct c { int v; } cc; } bb; };\n";
28217        let parsed = parse_cpp_declarations(source, "z.c");
28218        let declarations = parsed.declarations();
28219
28220        for tag in ["a", "b", "c"] {
28221            assert!(
28222                declarations
28223                    .iter()
28224                    .any(|unit| unit.is_class() && unit.fq_name() == tag),
28225                "expected a file-scope {tag}, got {declarations:?}"
28226            );
28227        }
28228        assert!(
28229            declarations
28230                .iter()
28231                .all(|unit| !unit.fq_name().contains('$')),
28232            "no level may keep a nested identity, got {declarations:?}"
28233        );
28234        // Each member still belongs to the aggregate that declares it.
28235        assert!(declarations.iter().any(|unit| unit.fq_name() == "a.bb"));
28236        assert!(declarations.iter().any(|unit| unit.fq_name() == "b.cc"));
28237        assert!(declarations.iter().any(|unit| unit.fq_name() == "c.v"));
28238    }
28239
28240    /// An enum tag is a tag; its enumerators stay members of the enum, which is
28241    /// what makes them ordinary identifiers at the enum's own (file) scope.
28242    #[test]
28243    #[hardwon(
28244        "9cce90a89128: C++ declaration recovery regressed for c file mints member list enum at file scope with its enumerators"
28245    )]
28246    fn c_file_mints_member_list_enum_at_file_scope_with_its_enumerators() {
28247        let source = "struct outer { enum color { RED, GREEN } c; };\n";
28248        let parsed = parse_cpp_declarations(source, "e.c");
28249        let declarations = parsed.declarations();
28250
28251        let color = declarations
28252            .iter()
28253            .find(|unit| unit.is_class() && unit.fq_name() == "color")
28254            .unwrap_or_else(|| panic!("expected a file-scope color enum, got {declarations:?}"));
28255        assert!(
28256            declarations
28257                .iter()
28258                .all(|unit| unit.fq_name() != "outer$color")
28259        );
28260        for enumerator in ["color.RED", "color.GREEN"] {
28261            assert!(
28262                declarations.iter().any(|unit| unit.fq_name() == enumerator),
28263                "expected {enumerator}, got {declarations:?}"
28264            );
28265        }
28266        let children = parsed
28267            .children
28268            .get(color)
28269            .unwrap_or_else(|| panic!("expected child edges for {color:?}"));
28270        assert!(
28271            ["color.RED", "color.GREEN"]
28272                .iter()
28273                .all(|name| children.iter().any(|child| child.fq_name() == *name)),
28274            "enumerators must hang off their enum: {children:?}"
28275        );
28276    }
28277
28278    #[test]
28279    #[hardwon(
28280        "9cce90a89128: C++ declaration recovery regressed for c file mints member list union at file scope"
28281    )]
28282    fn c_file_mints_member_list_union_at_file_scope() {
28283        let source = "struct outer { union inner { int a; float b; } item; };\n";
28284        let parsed = parse_cpp_declarations(source, "u.c");
28285        let declarations = parsed.declarations();
28286        assert!(
28287            declarations
28288                .iter()
28289                .any(|unit| unit.is_class() && unit.fq_name() == "inner"),
28290            "expected a file-scope inner union, got {declarations:?}"
28291        );
28292        assert!(
28293            declarations
28294                .iter()
28295                .all(|unit| unit.fq_name() != "outer$inner")
28296        );
28297        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.a"));
28298        assert!(declarations.iter().any(|unit| unit.fq_name() == "inner.b"));
28299    }
28300
28301    /// A tag declared in a namespace member list is not a file-scope tag: the
28302    /// nearest enclosing non-aggregate scope is the namespace.
28303    #[test]
28304    #[hardwon(
28305        "9cce90a89128: C++ declaration recovery regressed for c file member list tag lands in the enclosing namespace"
28306    )]
28307    fn c_file_member_list_tag_lands_in_the_enclosing_namespace() {
28308        let source = "namespace ns { struct outer { struct inner { int v; } i; }; }\n";
28309        let parsed = parse_cpp_declarations(source, "n.c");
28310        let declarations = parsed.declarations();
28311        let inner = declarations
28312            .iter()
28313            .find(|unit| unit.is_class() && unit.fq_name() == "ns.inner")
28314            .unwrap_or_else(|| panic!("expected ns.inner, got {declarations:?}"));
28315        assert_eq!(inner.package_name(), "ns");
28316        assert!(
28317            declarations
28318                .iter()
28319                .all(|unit| unit.fq_name() != "ns.outer$inner")
28320        );
28321    }
28322
28323    /// A tag declared inside a function body: the C reading mints nothing for
28324    /// it (C block scope is a separate capability), while the C++ reading
28325    /// mints the local class under its block identity and its nested class
28326    /// under the local class (#6902). Neither reading flattens either tag into
28327    /// file scope.
28328    #[test]
28329    #[hardwon(
28330        "9cce90a89128: C++ declaration recovery regressed for function local tags are unchanged in both dialects"
28331    )]
28332    fn function_local_tags_keep_block_scope_in_both_dialects() {
28333        let source =
28334            "void run(void) {\n  struct localtag { struct deeper { int v; } d; } item;\n}\n";
28335        for (name, local) in [("y.c", false), ("y.cpp", true)] {
28336            let parsed = parse_cpp_declarations(source, name);
28337            let declarations = parsed.declarations();
28338            assert!(
28339                declarations
28340                    .iter()
28341                    .any(|unit| unit.is_function() && unit.fq_name() == "run"),
28342                "{name}: {declarations:?}"
28343            );
28344            for tag in ["localtag", "deeper", "localtag$deeper"] {
28345                assert!(
28346                    declarations.iter().all(|unit| unit.fq_name() != tag),
28347                    "{name} must not mint {tag}, got {declarations:?}"
28348                );
28349            }
28350            let callable = declarations
28351                .iter()
28352                .find(|unit| unit.is_function() && unit.fq_name() == "run")
28353                .unwrap();
28354            let block = format!("run$owner${}$block$0", callable.declaration_id());
28355            for tag in [
28356                format!("{block}$localtag"),
28357                format!("{block}$localtag$deeper"),
28358                format!("{block}$localtag.d"),
28359            ] {
28360                assert_eq!(
28361                    declarations.iter().any(|unit| unit.fq_name() == tag),
28362                    local,
28363                    "{name}: {tag} in {declarations:?}"
28364                );
28365            }
28366        }
28367    }
28368
28369    /// An anonymous aggregate declares no tag, so the C dialect has nothing to
28370    /// re-scope: the typedef name is identical in both dialects.
28371    #[test]
28372    #[hardwon(
28373        "9cce90a89128: C++ declaration recovery regressed for anonymous typedef struct is identical in both dialects"
28374    )]
28375    fn anonymous_typedef_struct_is_identical_in_both_dialects() {
28376        let source = "typedef struct { int v; } T;\n";
28377        for name in ["t.c", "t.cpp"] {
28378            let parsed = parse_cpp_declarations(source, name);
28379            let declarations = parsed.declarations();
28380            assert!(
28381                declarations
28382                    .iter()
28383                    .any(|unit| unit.is_class() && unit.fq_name() == "T"),
28384                "{name}: {declarations:?}"
28385            );
28386        }
28387    }
28388
28389    /// The same principle for every anonymous aggregate the walk indexes, not
28390    /// just the typedef one: a promoted anonymous union in a member list, an
28391    /// anonymous struct given a receiver type by its declarator, a
28392    /// function-local aggregate, and one inside an inline member function
28393    /// body. The C++ reading used to mint none of these, so a pure C++
28394    /// workspace indexed no members at all (#3301).
28395    #[test]
28396    #[hardwon(
28397        "605f5c5219c4: C++ declaration recovery regressed for anonymous aggregate members are identical in both dialects"
28398    )]
28399    fn anonymous_aggregate_members_are_identical_in_both_dialects() {
28400        let sources = [
28401            "struct outer { union { int a; float b; }; };\n",
28402            "struct outer { struct { int *ops; } sock; };\n",
28403            "void run(void) { struct { int v; } item; }\n",
28404            "struct outer { int probe() { union { int raw; float scaled; } cell; return cell.raw; } };\n",
28405        ];
28406        for source in sources {
28407            let names = |name: &str| {
28408                parse_cpp_declarations(source, name)
28409                    .declarations()
28410                    .iter()
28411                    .map(|unit| (format!("{:?}", unit.kind()), unit.identifier().to_owned()))
28412                    .collect::<std::collections::BTreeSet<_>>()
28413            };
28414            let c_names = names("a.c");
28415            let cpp_names = names("a.cpp");
28416            assert_eq!(
28417                c_names, cpp_names,
28418                "an anonymous aggregate declares no tag, so both dialects read {source:?} the same"
28419            );
28420            assert!(
28421                c_names.iter().any(|(kind, _)| kind == "Field"),
28422                "{source:?} must index the anonymous aggregate's members, got {c_names:?}"
28423            );
28424        }
28425    }
28426
28427    #[test]
28428    #[hardwon(
28429        "31913fee42a0: C++ declaration recovery regressed for c anonymous aggregate members keep promoted and named receiver shapes"
28430    )]
28431    fn c_anonymous_aggregate_members_keep_promoted_and_named_receiver_shapes() {
28432        let source = "typedef struct { union { struct { struct socket_ops *ops; } sock; int other; }; } *PAL_HANDLE;\n";
28433        let parsed = parse_cpp_declarations(source, "socket.c");
28434        let declarations = parsed.declarations();
28435        assert_eq!(
28436            declarations
28437                .iter()
28438                .filter(|unit| unit.fq_name() == "PAL_HANDLE")
28439                .count(),
28440            1,
28441            "the typedef alias is the anonymous aggregate owner: {declarations:#?}"
28442        );
28443        for expected in [
28444            "PAL_HANDLE",
28445            "PAL_HANDLE.sock",
28446            "PAL_HANDLE$sock",
28447            "PAL_HANDLE$sock.ops",
28448        ] {
28449            assert!(
28450                declarations.iter().any(|unit| unit.fq_name() == expected),
28451                "expected {expected}, got {declarations:?}"
28452            );
28453        }
28454    }
28455
28456    /// Drive [`CppNamespaceForwardScan`] and the prefix scan it replaced over
28457    /// every (node, class-like name) pair a tree offers, and require the same
28458    /// answer from both.
28459    ///
28460    /// The release build has no `debug_assertions` agreement check, so this is
28461    /// what pins the two together there.  Both query orders are exercised:
28462    /// document order is what the walk does, and reverse order proves that a
28463    /// question about an earlier byte than one already answered is still
28464    /// filtered back to its own prefix rather than answered from the wider
28465    /// fold.
28466    ///
28467    /// Returns how many questions were answered with a namespace, so a fixture
28468    /// can assert it actually reached the path (#2754).
28469    fn namespace_forward_scan_agreement(source: &str) -> usize {
28470        let mut parser = tree_sitter::Parser::new();
28471        parser
28472            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28473            .unwrap();
28474        let tree = parser.parse(source, None).unwrap();
28475        let root = tree.root_node();
28476        let ancestry = ParentIndex::new(root);
28477
28478        let mut nodes = Vec::new();
28479        let mut names = std::collections::BTreeSet::new();
28480        let mut cursor = root.walk();
28481        let mut stack = vec![root];
28482        while let Some(node) = stack.pop() {
28483            if matches!(
28484                node.kind(),
28485                "class_specifier" | "struct_specifier" | "union_specifier"
28486            ) && let Some(name) = class_like_name(node, source, &ancestry)
28487            {
28488                names.insert(name);
28489            }
28490            nodes.push(node);
28491            stack.extend(node.named_children(&mut cursor));
28492        }
28493        nodes.sort_by_key(|node| (node.start_byte(), node.end_byte()));
28494        assert!(!names.is_empty(), "fixture declares no class-like name");
28495
28496        let mut answered = 0usize;
28497        for reversed in [false, true] {
28498            let mut scan = CppNamespaceForwardScan::default();
28499            let ordered: Vec<_> = if reversed {
28500                nodes.iter().rev().copied().collect()
28501            } else {
28502                nodes.clone()
28503            };
28504            answered = 0;
28505            for node in ordered {
28506                for name in &names {
28507                    scan.advance_to(root, node.start_byte(), source, &ancestry);
28508                    let carried = scan.unique_earlier_forward(name, node);
28509                    assert_eq!(
28510                        carried,
28511                        unique_earlier_cpp_namespace_forward(node, name, source, &ancestry),
28512                        "carried-forward scan and prefix scan disagree about {name} at \
28513                         {} node starting at byte {} (reversed order: {reversed})",
28514                        node.kind(),
28515                        node.start_byte()
28516                    );
28517                    answered += usize::from(carried.is_some());
28518                }
28519            }
28520        }
28521        answered
28522    }
28523
28524    /// A malformed namespace whose forward declarations are the only identity
28525    /// signal left for the class definitions tree-sitter pushed out to file
28526    /// scope.  Both recovered classes open the guard; only the first one is
28527    /// separated from the namespace by nothing but recovery trivia, so only the
28528    /// first one borrows.  The carried-forward scan has to reproduce both
28529    /// answers.
28530    const MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES: &str = r#"#define API
28531namespace ns {
28532class Widget;
28533class Gadget;
28534int x = ;
28535}
28536class API Widget {
28537public:
28538    void first();
28539};
28540class API Gadget {
28541public:
28542    void second();
28543};
28544"#;
28545
28546    #[test]
28547    #[hardwon(
28548        "d3b385cc16ff: C++ declaration recovery regressed for carried forward namespace scan answers what the prefix scan answers"
28549    )]
28550    fn carried_forward_namespace_scan_answers_what_the_prefix_scan_answers() {
28551        assert!(
28552            namespace_forward_scan_agreement(MALFORMED_NAMESPACE_WITH_TWO_RECOVERED_CLASSES) > 0,
28553            "the fixture must actually reach the namespace-borrow path"
28554        );
28555
28556        // Nothing here may be answered, and the two paths have to agree about
28557        // that too: a clean namespace is not an identity proof, two forwards of
28558        // one name are ambiguous rather than a guess, and a forward inside a
28559        // function body is not at namespace scope.
28560        for source in [
28561            "namespace clean {\nclass Widget;\n}\nclass API Widget {\npublic:\n    void method();\n};\n",
28562            r#"#define API
28563namespace ns {
28564class Widget;
28565class Widget;
28566int x = ;
28567}
28568class API Widget {
28569public:
28570    void method();
28571};
28572"#,
28573            r#"#define API
28574namespace ns {
28575void host() {
28576    class Widget;
28577}
28578int x = ;
28579}
28580class API Widget {
28581public:
28582    void method();
28583};
28584"#,
28585        ] {
28586            assert_eq!(
28587                namespace_forward_scan_agreement(source),
28588                0,
28589                "no borrow is justified here: {source}"
28590            );
28591        }
28592    }
28593
28594    /// Drive the region reparse and the whitespace-padded reparse it replaced
28595    /// over the same region and require identical trees.
28596    ///
28597    /// The release build has no `debug_assertions` agreement check, so this is
28598    /// what pins the two together there. Each body is reparsed at its own
28599    /// offset and again after a long prefix, because the prefix is the whole
28600    /// difference between the two techniques: the padded parse lexes it as
28601    /// whitespace, the included-range parse never sees it, and the tree has to
28602    /// come out the same either way (#2788).
28603    fn fragmented_class_reparse_agreement(body: &str) {
28604        for prefix in [
28605            String::new(),
28606            "// leading comment\n".to_string(),
28607            // A body starts just after its class head's `{`, which is normally
28608            // mid-line: the padded parse then has spaces before the region on
28609            // the region's own line, and the included-range parse has nothing
28610            // at all before it.
28611            "class Widget : public Base { ".to_string(),
28612            "namespace filler {\n".to_string()
28613                + &"struct Filler { int member; };\n".repeat(200)
28614                + "}\n",
28615            "namespace filler {\n".to_string()
28616                + &"struct Filler { int member; };\n".repeat(200)
28617                + "}\nclass Widget : public Base { ",
28618        ] {
28619            let source = format!("{prefix}{body}");
28620            let start = prefix.len();
28621            let end = source.len();
28622            let region = cpp_reparse_fragmented_class_body(&source, start, end)
28623                .expect("the region reparse must produce a tree");
28624            let padded = cpp_reparse_padded_class_body(&source, start, end)
28625                .expect("the padded reparse must produce a tree");
28626            assert_eq!(
28627                cpp_tree_shape(&region),
28628                cpp_tree_shape(&padded),
28629                "region and padded reparse disagree at offset {start} of {end} bytes"
28630            );
28631            assert_eq!(
28632                region.root_node().start_byte(),
28633                start,
28634                "the reparsed region keeps its original offsets"
28635            );
28636        }
28637    }
28638
28639    #[test]
28640    #[hardwon(
28641        "a4a47ac2395c: C++ declaration recovery regressed for the region reparse of a fragmented class body is the padded reparse"
28642    )]
28643    fn the_region_reparse_of_a_fragmented_class_body_is_the_padded_reparse() {
28644        // A conditional immediately after an access label: the shape the padded
28645        // technique was kept for, because the directive and its macro name
28646        // become an ERROR plus the following declaration's apparent type.
28647        fragmented_class_reparse_agreement(
28648            "public:\n#ifdef HAS_FEATURE\n   Widget(int value);\n#endif\n   void method();\n",
28649        );
28650        fragmented_class_reparse_agreement(
28651            "public:\n#if defined(A) || defined(B)\n   Widget();\n#else\n   Widget(int);\n#endif\n",
28652        );
28653        // The merged inline constructor and the nested fragmented bodies the
28654        // #938 recovery reads out of a reparse.
28655        fragmented_class_reparse_agreement(
28656            "public:\n   explicit Lookup_Error(std::string_view err) : Exception(err) {}\n\n                Lookup_Error(std::string_view type, std::string_view algo);\n",
28657        );
28658        fragmented_class_reparse_agreement(
28659            "public:\n   void first();\nclass Action {\npublic:\n   void second();\n",
28660        );
28661        // A body that is not member-shaped at all still has to reparse the same
28662        // way, because the admission gate reads the tree to reject it.
28663        fragmented_class_reparse_agreement("public:\n   value + other;\n   return value;\n");
28664    }
28665
28666    fn native_following_named_siblings<'tree>(node: Node<'tree>, source: &str) -> Vec<Node<'tree>> {
28667        let mut siblings = Vec::new();
28668        let mut anchor = node;
28669        while let Some(parent) = anchor.parent() {
28670            let at_translation_unit = parent.kind() == "translation_unit";
28671            let mut sibling = anchor.next_named_sibling();
28672            while let Some(current) = sibling {
28673                if at_translation_unit
28674                    && (current.kind() == "namespace_definition"
28675                        || (current.kind() == "function_definition"
28676                            && first_class_like_child(current).is_some()))
28677                {
28678                    return siblings;
28679                }
28680                siblings.push(current);
28681                if cpp_is_stray_close_brace(current, source) {
28682                    if let Some(semicolon) = current
28683                        .next_named_sibling()
28684                        .filter(|candidate| cpp_is_stray_semicolon(*candidate, source))
28685                    {
28686                        siblings.push(semicolon);
28687                    }
28688                    return siblings;
28689                }
28690                if current.start_byte() >= node.end_byte()
28691                    && matches!(current.kind(), "ERROR" | "labeled_statement")
28692                    && cpp_nested_stray_close_brace(current, source).is_some()
28693                {
28694                    return siblings;
28695                }
28696                sibling = current.next_named_sibling();
28697            }
28698            anchor = parent;
28699        }
28700        siblings
28701    }
28702
28703    #[test]
28704    #[hardwon(
28705        "1c7c8490fb76: C++ declaration recovery regressed for following named siblings cursor matches native traversal"
28706    )]
28707    fn following_named_siblings_cursor_matches_native_traversal() {
28708        // A damaged export-class head scatters members across container
28709        // siblings, and the anonymous punctuation forces the cursor to filter
28710        // children exactly as Node::next_named_sibling does. The grammar reads
28711        // a function-like export macro head since brokk-tree-sitter-cpp
28712        // 0.24.7, so the fixture uses an object-like macro before a base
28713        // clause, which still has no parse.
28714        let source = r#"
28715namespace api {
28716class PROJECT_API Widget : public virtual Base {
28717  public:
28718    Widget() {}
28719    void first();
28720};
28721enum After { Value };
28722}
28723"#;
28724        let mut parser = tree_sitter::Parser::new();
28725        parser
28726            .set_language(&tree_sitter_cpp::LANGUAGE.into())
28727            .unwrap();
28728        let tree = parser.parse(source, None).unwrap();
28729        let root = tree.root_node();
28730        assert!(root.has_error(), "fixture must remain damaged");
28731
28732        let ancestry = ParentIndex::new(root);
28733        let mut stack = vec![root];
28734        let mut checked = 0;
28735        let mut damaged = 0;
28736        let mut anonymous = 0;
28737        let mut nonempty_sequences = 0;
28738        let mut named_then_anonymous = 0;
28739        while let Some(node) = stack.pop() {
28740            let expected = native_following_named_siblings(node, source)
28741                .iter()
28742                .map(Node::id)
28743                .collect::<Vec<_>>();
28744            let actual = cpp_following_named_siblings(node, source, &ancestry)
28745                .iter()
28746                .map(Node::id)
28747                .collect::<Vec<_>>();
28748            assert_eq!(
28749                actual,
28750                expected,
28751                "cursor traversal diverged at byte {}",
28752                node.start_byte()
28753            );
28754
28755            let mut cursor = node.walk();
28756            let children = node.children(&mut cursor).collect::<Vec<_>>();
28757            stack.extend(children.iter().copied());
28758            checked += 1;
28759            damaged += usize::from(node.is_error() || node.is_missing());
28760            anonymous += usize::from(!node.is_named());
28761            nonempty_sequences += usize::from(!actual.is_empty());
28762            named_then_anonymous += usize::from(
28763                children
28764                    .windows(2)
28765                    .any(|pair| pair[0].is_named() && !pair[1].is_named()),
28766            );
28767        }
28768        assert!(checked > 0, "fixture must provide nodes to compare");
28769        assert!(damaged > 0, "fixture must cover damaged nodes");
28770        assert!(anonymous > 0, "fixture must cover anonymous nodes");
28771        assert!(
28772            nonempty_sequences > 0,
28773            "fixture must exercise following named siblings"
28774        );
28775        assert!(
28776            named_then_anonymous > 0,
28777            "fixture must exercise anonymous nodes following a named anchor"
28778        );
28779    }
28780
28781    #[test]
28782    #[hardwon(
28783        "69c2e1f7e41c: C++ declaration recovery regressed for a replacement that removes children drops the field index"
28784    )]
28785    fn a_replacement_removes_owned_enum_fields() {
28786        let source =
28787            "enum First { A };\nstruct Color { int RED; };\nstruct Color {};\nenum Color {};\n";
28788        let parsed = parse_cpp_declarations(source, "replaced-owner.hpp");
28789        let mut names: Vec<_> = parsed
28790            .declarations()
28791            .iter()
28792            .map(|unit| unit.fq_name())
28793            .collect();
28794        names.sort();
28795        assert_eq!(
28796            names,
28797            vec![
28798                "Color".to_string(),
28799                "First".to_string(),
28800                "First.A".to_string()
28801            ],
28802            "the replaced Color owns no field any more"
28803        );
28804    }
28805
28806    /// A recovery that re-declares what the file already declared mints
28807    /// nothing.
28808    ///
28809    /// The reparse walk replaces the outer `Widget`, which removes its method,
28810    /// and then re-creates that method from the region. Creation alone would
28811    /// call the method recovered; it was there before the recovery opened, so
28812    /// the recovered set is empty and only the region's own reparse window is
28813    /// recorded (#2787).
28814    #[test]
28815    #[hardwon(
28816        "a1556d125712: C++ declaration recovery regressed for a recovery that restores an existing declaration mints nothing"
28817    )]
28818    fn a_recovery_that_restores_an_existing_declaration_mints_nothing() {
28819        let source = "namespace demo { struct Widget { void doWork(); }; }\n\
28820                      BEGIN_NS\n\
28821                      namespace demo { struct Widget { void doWork(); }; }\n\
28822                      END_NS\n";
28823        let parsed = parse_cpp_declarations(source, "restored.cpp");
28824        let recovered: Vec<String> = parsed
28825            .materialization_records
28826            .iter()
28827            .filter_map(|record| match record {
28828                MaterializationRecord::RecoveredDeclaration { unit, .. } => Some(unit.fq_name()),
28829                _ => None,
28830            })
28831            .collect();
28832        assert!(
28833            recovered.is_empty(),
28834            "the region declares nothing the file did not already declare: {recovered:?}"
28835        );
28836        let mut names: Vec<String> = parsed
28837            .declarations()
28838            .iter()
28839            .map(|unit| unit.fq_name())
28840            .collect();
28841        names.sort();
28842        assert_eq!(
28843            names,
28844            vec![
28845                "demo".to_string(),
28846                "demo.Widget".to_string(),
28847                "demo.Widget.doWork".to_string(),
28848            ]
28849        );
28850    }
28851
28852    /// Four macro-sentinel recoveries in one file (#941). Each one must record
28853    /// exactly the declarations it minted -- not the ones an earlier recovery
28854    /// minted, and not the file's other declarations -- in start-byte order,
28855    /// and each record must carry its own reparse window (#2787).
28856    #[test]
28857    #[hardwon(
28858        "a1556d125712: C++ declaration recovery regressed for repeated sentinel recoveries record only what each one minted"
28859    )]
28860    fn repeated_sentinel_recoveries_record_only_what_each_one_minted() {
28861        let mut source = String::new();
28862        for index in 0..4 {
28863            let _ = write!(
28864                source,
28865                "BEGIN_NS\nnamespace demo{index} {{ struct Widget{index}                  {{ void doWork{index}(); }}; }}\nEND_NS\n"
28866            );
28867        }
28868        source.push_str("void outside() {}\n");
28869        let parsed = parse_cpp_declarations(&source, "repeated-sentinels.cpp");
28870
28871        let recovered: Vec<(String, (usize, usize))> = parsed
28872            .materialization_records
28873            .iter()
28874            .filter_map(|record| match record {
28875                MaterializationRecord::RecoveredDeclaration { recovery, unit } => {
28876                    Some((unit.fq_name(), (recovery.start_byte, recovery.end_byte)))
28877                }
28878                _ => None,
28879            })
28880            .collect();
28881
28882        let mut expected: Vec<(String, (usize, usize))> = Vec::new();
28883        for index in 0..4 {
28884            // The window the reparse covers: everything after the opening
28885            // sentinel token up to the newline before the closing one.
28886            let region = format!("namespace demo{index}");
28887            let region_start = source.find(&region).expect("each region is in the source");
28888            let start = source[..region_start]
28889                .rfind("BEGIN_NS")
28890                .expect("each region opens with a sentinel")
28891                + "BEGIN_NS".len();
28892            let end = start
28893                + source[start..]
28894                    .find("END_NS")
28895                    .expect("each region closes with a sentinel")
28896                - 1;
28897            let window = (start, end);
28898            for name in [
28899                format!("demo{index}"),
28900                format!("demo{index}.Widget{index}"),
28901                format!("demo{index}.Widget{index}.doWork{index}"),
28902            ] {
28903                expected.push((name, window));
28904            }
28905        }
28906        assert_eq!(
28907            recovered, expected,
28908            "each recovery records its own minted declarations, in order"
28909        );
28910        assert!(
28911            parsed
28912                .declarations()
28913                .iter()
28914                .any(|unit| unit.fq_name() == "outside"),
28915            "the declaration outside every region stays parsed and unrecovered"
28916        );
28917    }
28918
28919    /// `blocks` copies of one plain class-with-methods template. The class the
28920    /// question is about is always the first one, so the only thing that
28921    /// changes between two of these sources is how much unrelated tree
28922    /// surrounds it.
28923    fn repeated_class_blocks(blocks: usize) -> String {
28924        let mut source = String::from("namespace demo {\n");
28925        for index in 0..blocks {
28926            let _ = write!(
28927                source,
28928                "\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"
28929            );
28930        }
28931        source.push_str("\n}\n");
28932        source
28933    }
28934
28935    /// #1496: asking whether a recovered class shape claims one range must cost
28936    /// the path to that range, not a pass over the whole translation unit.
28937    ///
28938    /// The C++ inverse scan asks this once per candidate type reference, so a
28939    /// whole-tree walk makes one file's scan quadratic in its own size. Both
28940    /// sources here answer `None` -- these are plain classes that no recovery
28941    /// shape claims -- which is exactly the case that used to pay full price.
28942    #[test]
28943    #[hardwon(
28944        "4530afa0b64f: C++ declaration recovery regressed for recovered class body lookup cost does not grow with the rest of the file"
28945    )]
28946    fn recovered_class_body_lookup_cost_does_not_grow_with_the_rest_of_the_file() {
28947        let mut answers = Vec::new();
28948        let mut visits = Vec::new();
28949        let mut node_counts = Vec::new();
28950        for blocks in [200usize, 400] {
28951            let source = repeated_class_blocks(blocks);
28952            let mut parser = tree_sitter::Parser::new();
28953            parser
28954                .set_language(&tree_sitter_cpp::LANGUAGE.into())
28955                .unwrap();
28956            let tree = parser.parse(&source, None).unwrap();
28957            let start_byte = source.find("class Widget0 ").expect("first class");
28958            let end_byte = start_byte
28959                + source[start_byte..]
28960                    .find("};")
28961                    .expect("first class terminator")
28962                + "};".len();
28963            let range = Range {
28964                start_byte,
28965                end_byte,
28966                start_line: 0,
28967                end_line: 0,
28968            };
28969            reset_recovered_class_body_node_visits_for_test();
28970            let recovered_export_classes =
28971                CppRecoveredExportClassIndex::build(tree.root_node(), &source);
28972            answers.push(recovered_class_body_at(
28973                &recovered_export_classes,
28974                tree.root_node(),
28975                &source,
28976                "Widget0",
28977                &range,
28978            ));
28979            visits.push(recovered_class_body_node_visits_for_test());
28980            let mut nodes = 0usize;
28981            let mut stack = vec![tree.root_node()];
28982            while let Some(node) = stack.pop() {
28983                nodes += 1;
28984                let mut cursor = node.walk();
28985                stack.extend(node.named_children(&mut cursor));
28986            }
28987            node_counts.push(nodes);
28988        }
28989
28990        assert_eq!(
28991            answers,
28992            vec![None, None],
28993            "no recovered shape claims a plain class"
28994        );
28995        assert_eq!(
28996            visits[0], visits[1],
28997            "the walk must follow the range's own path, so doubling the unrelated \
28998             classes must not change the node count: {visits:?} over trees of \
28999             {node_counts:?} nodes"
29000        );
29001        assert!(
29002            visits[1] * 20 < node_counts[1],
29003            "the walk must stay far below one pass over the tree: {visits:?} over \
29004             trees of {node_counts:?} nodes"
29005        );
29006    }
29007
29008    #[test]
29009    #[hardwon("#2982: C MBEDTLS_PRIVATE fields lost direct and chained receiver types")]
29010    fn mbedtls_private_pointer_field_keeps_its_structured_name_and_type() {
29011        let source = "struct ssl { struct handshake *MBEDTLS_PRIVATE(handshake); };";
29012        let mut parser = tree_sitter::Parser::new();
29013        parser
29014            .set_language(&tree_sitter_cpp::LANGUAGE.into())
29015            .expect("C++ grammar");
29016        let tree = parser.parse(source, None).expect("fixture tree");
29017        let mut stack = vec![tree.root_node()];
29018        let mut recovered = None;
29019        while let Some(node) = stack.pop() {
29020            if node.kind() == "field_declaration"
29021                && let Some(field) = recovered_function_like_field_declarator(node, source)
29022            {
29023                recovered = Some((node, field.name));
29024                break;
29025            }
29026            let mut cursor = node.walk();
29027            stack.extend(node.named_children(&mut cursor));
29028        }
29029        let (declaration, name) = recovered.unwrap_or_else(|| {
29030            panic!(
29031                "pointer-wrapped macro field was not recovered: {}",
29032                tree.root_node().to_sexp()
29033            )
29034        });
29035        assert_eq!(node_text(name, source), "handshake");
29036        let recovered =
29037            recovered_function_like_field_declarator(declaration, source).expect("recovered field");
29038        assert_eq!(recovered.pointer_depth(), 1);
29039        assert_eq!(
29040            render_cpp_field_signature(declaration, name, source),
29041            "struct handshake * handshake;"
29042        );
29043    }
29044
29045    #[test]
29046    #[hardwon(
29047        "4f0531e0e865: C++ parser recovery previously lost declaration ownership, member scope, or source identity"
29048    )]
29049    fn pyobject_head_pointer_field_keeps_its_structured_name_and_type() {
29050        let source = "struct Holder { PyObject_HEAD ImagingObject *image; };";
29051        let mut parser = tree_sitter::Parser::new();
29052        parser
29053            .set_language(&tree_sitter_cpp::LANGUAGE.into())
29054            .expect("C++ grammar");
29055        let tree = parser.parse(source, None).expect("fixture tree");
29056        // The macro-declaration grammar reads `PyObject_HEAD` as a
29057        // `macro_modifier`, so the member arrives as an ordinary clean field
29058        // declaration rather than the recovery shape the ERROR-based reader
29059        // exists for.
29060        let mut stack = vec![tree.root_node()];
29061        let mut declaration = None;
29062        while let Some(node) = stack.pop() {
29063            if node.kind() == "field_declaration" {
29064                declaration = Some(node);
29065                break;
29066            }
29067            let mut cursor = node.walk();
29068            stack.extend(node.named_children(&mut cursor));
29069        }
29070        let declaration = declaration.unwrap_or_else(|| {
29071            panic!(
29072                "the pointer field must arrive as an ordinary field declaration: {}",
29073                tree.root_node().to_sexp()
29074            )
29075        });
29076        let declarator = declaration
29077            .child_by_field_name("declarator")
29078            .expect("pointer field declarator");
29079        assert_eq!(node_text(declarator, source), "*image");
29080        assert_eq!(
29081            render_cpp_field_signature(declaration, declarator, source),
29082            "PyObject_HEAD ImagingObject * image;",
29083            "the macro marker is retained as modifier provenance, the same \
29084             treatment an attribute or storage-class specifier gets"
29085        );
29086        assert_eq!(
29087            crate::source_facts::capture_field_type(declaration, "image", source),
29088            (
29089                Some(
29090                    brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact {
29091                        type_text: "ImagingObject".to_owned(),
29092                        indirection: 1,
29093                        binds_indirectly: true,
29094                        template_arguments: None,
29095                    }
29096                ),
29097                false,
29098            ),
29099            "canonical field facts must use the declared type, not the macro token"
29100        );
29101    }
29102
29103    #[test]
29104    fn class_specifier_in_a_c_file_keeps_cpp_nesting() {
29105        let source = "class outer { class inner { int v; }; };\n";
29106        let c_parsed = parse_cpp_declarations(source, "k.c");
29107        let cpp_parsed = parse_cpp_declarations(source, "k.cpp");
29108        let c_declarations = c_parsed.declarations();
29109        let cpp_declarations = cpp_parsed.declarations();
29110        assert!(
29111            c_declarations
29112                .iter()
29113                .any(|unit| unit.is_class() && unit.fq_name() == "outer$inner"),
29114            "{c_declarations:?}"
29115        );
29116        assert_eq!(
29117            c_declarations
29118                .iter()
29119                .map(|unit| unit.fq_name())
29120                .collect::<std::collections::BTreeSet<_>>(),
29121            cpp_declarations
29122                .iter()
29123                .map(|unit| unit.fq_name())
29124                .collect::<std::collections::BTreeSet<_>>()
29125        );
29126    }
29127    #[test]
29128    fn callable_metadata_records_c_receiver_contracts_structurally() {
29129        const SOURCE: &str = concat!(
29130            "static long accumulate(const long *lines, int count) { return count; }\n",
29131            "\n",
29132            "long total(const long *lines, int count) { return accumulate(lines, count); }\n",
29133        );
29134        let parsed = parse_cpp_declarations(SOURCE, "shop.c");
29135
29136        assert_eq!(
29137            recorded_callable_modifiers(&parsed),
29138            vec![
29139                "accumulate decl_only=false static=false constructor=false \
29140                 visibility=Unknown"
29141                    .to_string(),
29142                "total decl_only=false static=false constructor=false visibility=Unknown"
29143                    .to_string(),
29144            ],
29145        );
29146    }
29147    #[test]
29148    fn callable_metadata_records_cpp_receiver_contracts_structurally() {
29149        const SOURCE: &str = concat!(
29150            "namespace shop {\n",
29151            "\n",
29152            "class Client {\n",
29153            "public:\n",
29154            "    Client();\n",
29155            "    void send(int order);\n",
29156            "    static int count();\n",
29157            "protected:\n",
29158            "    virtual int audit(int n) const { return n; }\n",
29159            "private:\n",
29160            "    int tally(int n) { return n; }\n",
29161            "};\n",
29162            "\n",
29163            "struct Ledger {\n",
29164            "    void record(int n) { (void)n; }\n",
29165            "};\n",
29166            "\n",
29167            "Client::Client() {}\n",
29168            "void Client::send(int order) { (void)order; }\n",
29169            "int Client::count() { return 0; }\n",
29170            "\n",
29171            "int total(int order) { return order; }\n",
29172            "\n",
29173            "}\n",
29174        );
29175        let parsed = parse_cpp_declarations(SOURCE, "shop.cpp");
29176
29177        assert_eq!(
29178            recorded_callable_modifiers(&parsed),
29179            vec![
29180                "shop.Client.Client decl_only=false static=false constructor=true \
29181                 visibility=Unknown"
29182                    .to_string(),
29183                "shop.Client.Client decl_only=true static=false constructor=true \
29184                 visibility=Public"
29185                    .to_string(),
29186                "shop.Client.audit decl_only=false static=false constructor=false \
29187                 visibility=Protected"
29188                    .to_string(),
29189                "shop.Client.count decl_only=false static=false constructor=false \
29190                 visibility=Unknown"
29191                    .to_string(),
29192                "shop.Client.count decl_only=true static=true constructor=false \
29193                 visibility=Public"
29194                    .to_string(),
29195                "shop.Client.send decl_only=false static=false constructor=false \
29196                 visibility=Unknown"
29197                    .to_string(),
29198                "shop.Client.send decl_only=true static=false constructor=false \
29199                 visibility=Public"
29200                    .to_string(),
29201                "shop.Client.tally decl_only=false static=false constructor=false \
29202                 visibility=Private"
29203                    .to_string(),
29204                "shop.Ledger.record decl_only=false static=false constructor=false \
29205                 visibility=Public"
29206                    .to_string(),
29207                "shop.total decl_only=false static=false constructor=false visibility=Unknown"
29208                    .to_string(),
29209            ],
29210        );
29211    }
29212
29213    #[test]
29214    fn reparsed_member_signature_grammar_contract() {
29215        use crate::grammar_test_support::{nodes, tree};
29216
29217        // These are class-member fragments reparsed at translation-unit scope,
29218        // exactly the boundary the recovery adapter receives from the grammar.
29219        // Identifier-only annotation errors retain a complete declaration;
29220        // literal payloads and missing signature tokens cannot be admitted.
29221        for (source, expected, error_children, missing_close) in [
29222            ("int f(int n) const { return n; }", true, vec![], false),
29223            (
29224                "int f(int n) const nonneg { return n; }",
29225                true,
29226                vec!["identifier"],
29227                false,
29228            ),
29229            (
29230                "int f(int n) ATTR 42 { return n; }",
29231                false,
29232                vec!["identifier", "number_literal"],
29233                false,
29234            ),
29235            (
29236                "int f(int n = 42 43) { return n; }",
29237                false,
29238                vec!["number_literal"],
29239                false,
29240            ),
29241            ("int f(int n { return n; }", false, vec![], true),
29242        ] {
29243            let parsed = tree(source);
29244            let root = parsed.root_node();
29245            let function = root.named_child(0).unwrap();
29246            assert_eq!(
29247                function.kind(),
29248                "function_definition",
29249                "{source}: {}",
29250                root.to_sexp()
29251            );
29252            let declarator = function.child_by_field_name("declarator").unwrap();
29253            assert_eq!(declarator.kind(), "function_declarator");
29254            let parameters = declarator.child_by_field_name("parameters").unwrap();
29255            assert_eq!(parameters.kind(), "parameter_list");
29256            assert_eq!(
29257                function.child_by_field_name("body").unwrap().kind(),
29258                "compound_statement"
29259            );
29260            let all_nodes = nodes(function);
29261            let errors: Vec<_> = all_nodes
29262                .iter()
29263                .filter(|node| node.kind() == "ERROR")
29264                .collect();
29265            let kinds: Vec<_> = errors
29266                .iter()
29267                .flat_map(|error| {
29268                    error
29269                        .named_children(&mut error.walk())
29270                        .map(|child| child.kind())
29271                        .collect::<Vec<_>>()
29272                })
29273                .collect();
29274            assert_eq!(kinds, error_children, "{source}: {}", root.to_sexp());
29275            assert_eq!(
29276                all_nodes
29277                    .iter()
29278                    .any(|node| node.is_missing() && node.kind() == ")"),
29279                missing_close,
29280                "{source}"
29281            );
29282            assert_eq!(
29283                cpp_reparsed_members_are_indexable(root, source),
29284                expected,
29285                "{source}: {}",
29286                root.to_sexp()
29287            );
29288        }
29289    }
29290    #[test]
29291    fn qualified_and_template_declarator_name_grammar_contract() {
29292        use crate::grammar_test_support::{nodes, tree};
29293        let source = "void select(void (Left::*left)(), void (Right::*right)());\ntemplate<class T> T make();\ntemplate<> int make<int>() { return 0; }\n";
29294        let parsed = tree(source);
29295        let all = nodes(parsed.root_node());
29296        for (written, owner) in [("Left::*left", "Left"), ("Right::*right", "Right")] {
29297            let qualified = all
29298                .iter()
29299                .copied()
29300                .find(|node| {
29301                    node.kind() == "qualified_identifier" && node_text(*node, source) == written
29302                })
29303                .unwrap_or_else(|| panic!("missing {written}: {}", parsed.root_node().to_sexp()));
29304            let scope = qualified.child_by_field_name("scope").unwrap();
29305            assert_eq!(node_text(scope, source), owner);
29306            assert!(qualified.child_by_field_name("name").is_some());
29307            assert_eq!(extract_declarator_name(qualified, source), written);
29308        }
29309        let function = all
29310            .iter()
29311            .copied()
29312            .find(|node| {
29313                node.kind() == "function_declarator"
29314                    && node
29315                        .child_by_field_name("declarator")
29316                        .is_some_and(|name| name.kind() == "template_function")
29317            })
29318            .unwrap_or_else(|| {
29319                panic!(
29320                    "missing template declarator: {}",
29321                    parsed.root_node().to_sexp()
29322                )
29323            });
29324        let template = function.child_by_field_name("declarator").unwrap();
29325        let name = template.child_by_field_name("name").unwrap();
29326        assert_eq!(name.kind(), "identifier");
29327        assert_eq!(node_text(name, source), "make");
29328        assert_eq!(
29329            template.child_by_field_name("arguments").unwrap().kind(),
29330            "template_argument_list"
29331        );
29332        assert_eq!(cpp_function_declarator_name_node(function), Some(name));
29333    }
29334
29335    fn declaration_expansion_inventory(file: &ParsedFile) -> Vec<String> {
29336        let mut declarations = file
29337            .declarations()
29338            .iter()
29339            .map(|unit| {
29340                let callable = unit.is_function().then(|| {
29341                    let mut labels = file
29342                        .signature_metadata
29343                        .get(unit)
29344                        .into_iter()
29345                        .flatten()
29346                        .map(|metadata| {
29347                            metadata
29348                                .parameters()
29349                                .iter()
29350                                .map(|parameter| parameter.label())
29351                                .collect::<Vec<_>>()
29352                        })
29353                        .collect::<Vec<_>>();
29354                    labels.sort();
29355                    (unit.signature(), labels)
29356                });
29357                let fields = unit.is_field().then(|| {
29358                    let mut signatures = file.signatures.get(unit).cloned().unwrap_or_default();
29359                    signatures.sort();
29360                    signatures.dedup();
29361                    signatures
29362                });
29363                format!(
29364                    "{:?} {} {fields:?} {callable:?}",
29365                    unit.kind(),
29366                    unit.fq_name()
29367                )
29368            })
29369            .collect::<Vec<_>>();
29370        declarations.sort();
29371        declarations
29372    }
29373
29374    /// Metamorphic oracle: erasing declaration decorators, expanding field-list
29375    /// macros, and spelling a namespace explicitly preserve declaration identity.
29376    /// Compare the complete inventory so a recovered initializer or macro token
29377    /// cannot pass merely because the intended callable was also recovered.
29378    #[test]
29379    fn recovery_declarations_agree_with_explicit_expansions() {
29380        let cases = [
29381            (
29382                "decorated constructors",
29383                "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",
29384                "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",
29385            ),
29386            (
29387                "namespace sentinel",
29388                "BEGIN_NS\nnamespace demo { class Widget { public: int first; void method(); }; }\nEND_NS\n",
29389                "namespace demo { class Widget { public: int first; void method(); }; }\n",
29390            ),
29391            (
29392                "wrapped prototypes",
29393                "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",
29394                "struct library_context * library_init_from_file(const char * path_model);\nint next_function(int value);\n",
29395            ),
29396            (
29397                "namespace forward across directives",
29398                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nclass API Widget { public: void method(); };\n",
29399                "#define API\nnamespace ns { class Widget; int x = ; }\n#pragma once\n#include \"local.h\"\nnamespace ns { class Widget { public: void method(); }; }\n",
29400            ),
29401            (
29402                "local forward does not lend namespace",
29403                "namespace ns { void host() { class Widget; int x = ; } }\nclass API Widget { public: void method(); };\n",
29404                "namespace ns { void host() { class Widget; int x = ; } }\nclass Widget { public: void method(); };\n",
29405            ),
29406            (
29407                "object declarator does not lend namespace",
29408                "namespace ns { struct Widget (object); int x = ; }\nclass API Widget { public: void method(); };\n",
29409                "namespace ns { struct Widget (object); int x = ; }\nclass Widget { public: void method(); };\n",
29410            ),
29411            (
29412                "const template return",
29413                r###"template<class T> struct C { API const T f() { return {}; } };"###,
29414                r###"template<class T> struct C { const T f() { return {}; } };"###,
29415            ),
29416            (
29417                "collapsed struct and union",
29418                r###"#define FIRST int first;
29419#define SECOND int first; int second;
29420union One { FIRST SECOND
29421};
29422struct Two { FIRST SECOND
29423};
29424struct Tail { UNDEFINED
29425};
29426"###,
29427                r###"#define FIRST int first;
29428#define SECOND int first; int second;
29429union One { int first; int first; int second;
29430};
29431struct Two { int first; int first; int second;
29432};
29433struct Tail { UNDEFINED
29434};
29435"###,
29436            ),
29437            (
29438                "namespaced_macro_fragment_keeps_prefix_members_and_following_classes",
29439                r###"
29440#pragma once
29441#define DEMO_DEPRECATED(message)
29442namespace demo {
29443struct Base {
29444    static int aligned(int value) { return value; }
29445    int legacy(int value) const
29446        DEMO_DEPRECATED("use replacement()") { return value; }
29447    int replacement() const;
29448    void run(int value);
29449};
29450struct OtherBase {
29451    void run(int value);
29452    static int aligned(int value) { return value; }
29453};
29454struct Derived : Base {};
29455struct Override : Base {
29456    void run(int value);
29457    static int aligned(int value) { return value; }
29458};
29459struct RecoveredOverride : Base {
29460    int legacy(int value) const
29461        DEMO_DEPRECATED("use replacement()") { return value; }
29462    void run(int value);
29463};
29464struct Hidden : Base {
29465    void run(int first, int second);
29466    static int aligned(int first, int second) { return first + second; }
29467};
29468struct Ambiguous : Base, OtherBase {};
29469}
29470struct Global {};
29471"###,
29472                r###"
29473#pragma once
29474#define DEMO_DEPRECATED(message)
29475namespace demo {
29476struct Base {
29477    static int aligned(int value) { return value; }
29478    int legacy(int value) const
29479         { return value; }
29480    int replacement() const;
29481    void run(int value);
29482};
29483struct OtherBase {
29484    void run(int value);
29485    static int aligned(int value) { return value; }
29486};
29487struct Derived : Base {};
29488struct Override : Base {
29489    void run(int value);
29490    static int aligned(int value) { return value; }
29491};
29492struct RecoveredOverride : Base {
29493    int legacy(int value) const
29494         { return value; }
29495    void run(int value);
29496};
29497struct Hidden : Base {
29498    void run(int first, int second);
29499    static int aligned(int first, int second) { return first + second; }
29500};
29501struct Ambiguous : Base, OtherBase {};
29502}
29503struct Global {};
29504"###,
29505            ),
29506            (
29507                "sentinel union",
29508                r###"BEGIN_NS
29509union Storage {int value; char byte;};
29510END_NS
29511"###,
29512                r###"union Storage {int value; char byte;};
29513"###,
29514            ),
29515            (
29516                "compact sentinel class",
29517                r###"BEGIN_NS
29518class Widget {int first; void method();};
29519END_NS
29520"###,
29521                r###"class Widget {int first; void method();};
29522"###,
29523            ),
29524            (
29525                "three decorators",
29526                "class C { FIRST SECOND THIRD void f() {} };",
29527                "class C { void f() {} };",
29528            ),
29529            (
29530                "overlapping field lists",
29531                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { FIRST SECOND int\n tail; };\n",
29532                "#define FIRST int first;\n#define SECOND int first; int second;\nstruct Handle { int first; int first; int second; int\n tail; };\n",
29533            ),
29534            (
29535                "identical redefinition",
29536                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS int tail;\n};\n",
29537                "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first; int tail;\n};\n",
29538            ),
29539            (
29540                "unconditional reset",
29541                "#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",
29542                "#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",
29543            ),
29544        ];
29545        for (label, source_lf, expanded_lf) in cases {
29546            for (source, expanded) in [
29547                (source_lf.to_owned(), expanded_lf.to_owned()),
29548                (
29549                    source_lf.replace('\n', "\r\n"),
29550                    expanded_lf.replace('\n', "\r\n"),
29551                ),
29552            ] {
29553                let tree = crate::grammar_test_support::tree(&source);
29554                let nodes = crate::grammar_test_support::nodes(tree.root_node());
29555                match label {
29556                    "decorated constructors" => assert!(
29557                        nodes.iter().any(|node| {
29558                            node.kind() == "ERROR"
29559                                && node
29560                                    .named_child(0)
29561                                    .is_some_and(|head| head.kind() == "macro_modifier_declarator")
29562                        }),
29563                        "{}",
29564                        tree.root_node().to_sexp()
29565                    ),
29566                    "wrapped prototypes" => assert!(
29567                        tree.root_node().named_child(0).is_some_and(|node| {
29568                            node.kind() == "ERROR"
29569                                && node
29570                                    .named_child(0)
29571                                    .is_some_and(|head| head.kind() == "function_declarator")
29572                        }),
29573                        "{}",
29574                        tree.root_node().to_sexp()
29575                    ),
29576                    "collapsed struct and union" => assert!(
29577                        nodes.iter().any(|node| {
29578                            node.kind() == "union"
29579                                && node.parent().is_some_and(|parent| parent.kind() == "ERROR")
29580                        }),
29581                        "{}",
29582                        tree.root_node().to_sexp()
29583                    ),
29584                    "object declarator does not lend namespace" => assert!(
29585                        nodes
29586                            .iter()
29587                            .any(|node| { node.kind() == "parenthesized_declarator" }),
29588                        "{}",
29589                        tree.root_node().to_sexp()
29590                    ),
29591                    _ => {}
29592                }
29593                let parsed = parse_cpp_declarations(&source, "expansion.h");
29594                let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29595                assert_eq!(
29596                    declaration_expansion_inventory(&parsed),
29597                    declaration_expansion_inventory(&explicit),
29598                    "{label}"
29599                );
29600                for unit in parsed.declarations() {
29601                    for range in parsed.declaration_ranges(unit) {
29602                        assert!(
29603                            range.start_byte < range.end_byte,
29604                            "{label}: {unit:?}: {range:?}"
29605                        );
29606                        for (offset, line) in [
29607                            (range.start_byte, range.start_line),
29608                            (range.end_byte, range.end_line),
29609                        ] {
29610                            assert_eq!(
29611                                line,
29612                                source[..offset]
29613                                    .bytes()
29614                                    .filter(|byte| *byte == b'\n')
29615                                    .count()
29616                                    + 1,
29617                                "{label}: {unit:?}: {range:?}"
29618                            );
29619                        }
29620                    }
29621                }
29622            }
29623        }
29624    }
29625
29626    #[test]
29627    #[hardwon(
29628        "5e79db920891: quicksilver round 4 survivor tests: labelled recovered constructors lost pointer parameters"
29629    )]
29630    fn recovered_class_constructor_agrees_with_explicit_expansion() {
29631        let source = "class API(1) value { public: value(pn_data_t* d); int following(); };";
29632        let tree = crate::grammar_test_support::tree(source);
29633        crate::grammar_test_support::assert_function_like_export_class_head(&tree, source, "value");
29634        let actual = parse_cpp_declarations(source, "constructor.h");
29635        let explicit = parse_cpp_declarations(
29636            "class value { public: value(pn_data_t* d); int following(); };",
29637            "constructor.h",
29638        );
29639        assert_eq!(function_identities(&actual), function_identities(&explicit));
29640    }
29641
29642    #[test]
29643    #[hardwon(
29644        "5e79db920891: quicksilver round 4 survivor tests: recovered base initializers became methods"
29645    )]
29646    fn recovered_base_initializer_is_not_a_declaration() {
29647        let source = r###"
29648namespace api {
29649
29650/**
29651* Doc comment
29652*/
29653class PROJECT_PUBLIC_API(2, 0) VirtualBased : public virtual BaseKey {
29654   public:
29655      /**
29656      * Construct from a point.
29657      */
29658      VirtualBased(const Group& group, const Point& point) : BaseKey(group, point) {}
29659
29660#if defined(PROJECT_HAS_LEGACY_POINT)
29661      /**
29662      * Construct from a legacy point.
29663      */
29664      VirtualBased(const Group& group, const LegacyPoint& point) : BaseKey(group, point) {}
29665#endif
29666
29667      std::string algo_name() const override;
29668
29669      AlgorithmIdentifier algorithm_identifier() const override;
29670};
29671
29672}
29673"###;
29674        let tree = crate::grammar_test_support::tree(source);
29675        crate::grammar_test_support::assert_function_like_export_class_head(
29676            &tree,
29677            source,
29678            "VirtualBased",
29679        );
29680        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
29681        let actual = parse_cpp_declarations(source, "virtual-base.h");
29682        let explicit = parse_cpp_declarations(&expanded, "virtual-base.h");
29683        assert_eq!(
29684            declaration_expansion_inventory(&actual),
29685            declaration_expansion_inventory(&explicit)
29686        );
29687    }
29688
29689    #[test]
29690    #[hardwon("#6902: a semicolon-free macro envelope hid the records and restore after it")]
29691    fn recovered_semicolon_free_macro_envelope_agrees_with_expansion() {
29692        let source = r###"struct Aggregate;
29693
29694#define NOARG
29695LIST_TYPES(deps, struct Aggregate *, NOARG, 32)
29696#undef NOARG
29697
29698struct Context { int value; };
29699struct Visit { int value; };
29700
29701LIST_DECLS(static, deps, struct Aggregate *, unused)
29702LIST_CODE(static, deps, struct Aggregate *, 0, alloc, free)
29703
29704static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29705
29706static struct Context *restore(struct Aggregate *value) ATTRIBUTE;
29707static struct Context *restore(struct Aggregate *value) {
29708  return (struct Context *) value;
29709}
29710
29711static void caller(struct Context *ctx, struct Visit *visit) {
29712  target(ctx, 0, 0, visit); /* recovered prototype */
29713}
29714
29715static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29716  (void) ctx;
29717  (void) first;
29718  (void) second;
29719  (void) visit;
29720}
29721"###;
29722        let expanded = r###"struct Aggregate;
29723
29724#define NOARG
29725#undef NOARG
29726
29727struct Context { int value; };
29728struct Visit { int value; };
29729
29730
29731static void target(struct Context *ctx, int first, int second, struct Visit *visit);
29732
29733static struct Context *restore(struct Aggregate *value);
29734static struct Context *restore(struct Aggregate *value) {
29735  return (struct Context *) value;
29736}
29737
29738static void caller(struct Context *ctx, struct Visit *visit) {
29739  target(ctx, 0, 0, visit); /* recovered prototype */
29740}
29741
29742static void target(struct Context *ctx, int first, int second, struct Visit *visit) {
29743  (void) ctx;
29744  (void) first;
29745  (void) second;
29746  (void) visit;
29747}
29748"###;
29749        let tree = crate::grammar_test_support::tree(source);
29750        assert!(
29751            tree.root_node().has_error(),
29752            "{}",
29753            tree.root_node().to_sexp()
29754        );
29755        let actual = parse_cpp_declarations(source, "expansion.h");
29756        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29757        assert_eq!(
29758            declaration_expansion_inventory(&actual),
29759            declaration_expansion_inventory(&explicit)
29760        );
29761    }
29762
29763    #[test]
29764    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29765    fn recovered_sentinel_alias_agrees_with_expansion() {
29766        let source = r###"namespace absl {
29767ABSL_NAMESPACE_BEGIN namespace container_internal {
29768
29769class raw_hash_set : public Base {
29770 public:
29771  using value_type = int;
29772
29773  template <class U,
29774            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29775  void insert(U value) { (void)value; }
29776
29777  struct InsertSlot {
29778    raw_hash_set& s;
29779  };
29780};
29781
29782}
29783ABSL_NAMESPACE_END
29784}"###;
29785        let expanded = r###"namespace absl {
29786namespace container_internal {
29787
29788class raw_hash_set : public Base {
29789 public:
29790  using value_type = int;
29791
29792  template <class U>
29793  void insert(U value) { (void)value; }
29794
29795  struct InsertSlot {
29796    raw_hash_set& s;
29797  };
29798};
29799
29800}
29801
29802}"###;
29803        let tree = crate::grammar_test_support::tree(source);
29804        assert!(
29805            tree.root_node().has_error(),
29806            "{}",
29807            tree.root_node().to_sexp()
29808        );
29809        let actual = parse_cpp_declarations(source, "expansion.h");
29810        let explicit = parse_cpp_declarations(expanded, "expansion.h");
29811        assert_eq!(
29812            declaration_expansion_inventory(&actual),
29813            declaration_expansion_inventory(&explicit)
29814        );
29815    }
29816
29817    /// The structured alias facts the resolver and navigation read, for one
29818    /// declaration of `fq_name`.
29819    fn sentinel_alias_facts(file: &ParsedFile, fq_name: &str) -> String {
29820        let units = file
29821            .declarations()
29822            .iter()
29823            .filter(|unit| unit.fq_name() == fq_name)
29824            .collect::<Vec<_>>();
29825        let [unit] = units.as_slice() else {
29826            panic!("expected one {fq_name}: {:#?}", file.declarations());
29827        };
29828        let facts = file.source_facts.as_ref().unwrap().cpp.as_ref().unwrap();
29829        let fact = file
29830            .source_declaration_units
29831            .iter()
29832            .filter(|(_, linked)| linked == *unit)
29833            .map(|(declaration, _)| {
29834                facts
29835                    .declarations
29836                    .iter()
29837                    .find(|fact| fact.declaration == *declaration)
29838                    .expect("alias source fact")
29839            })
29840            .map(|fact| {
29841                (
29842                    &fact.alias_target,
29843                    &fact.alias_target_text,
29844                    &fact.lexical_path,
29845                    &fact.guard_requirements,
29846                    &fact.file_scope_alias,
29847                )
29848            })
29849            .collect::<Vec<_>>();
29850        let parents = file
29851            .children
29852            .iter()
29853            .filter(|(_, children)| children.contains(unit))
29854            .map(|(parent, _)| parent.fq_name())
29855            .collect::<Vec<_>>();
29856        format!(
29857            "{:?} alias={} parents={parents:?} signatures={:?} metadata={:?} template={:?} fact={fact:?}",
29858            unit.kind(),
29859            file.type_aliases.contains(unit),
29860            file.signatures.get(unit),
29861            file.signature_metadata.get(unit),
29862            file.cpp_template_metadata.get(unit),
29863        )
29864    }
29865
29866    #[test]
29867    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29868    fn recovered_sentinel_aliases_keep_targets_ranges_and_owner() {
29869        let source = r###"namespace absl {
29870ABSL_NAMESPACE_BEGIN namespace container_internal {
29871
29872class raw_hash_set : public Base {
29873 public:
29874  using value_type = int;
29875  template <class T>
29876  using rebind = Base<T>;
29877  using key_type = value_type;
29878#ifdef ABSL_HAVE_SLOT
29879  using slot_type = long;
29880#endif
29881
29882  template <class U,
29883            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29884  void insert(U value) { (void)value; }
29885
29886  struct InsertSlot {
29887    raw_hash_set& s;
29888  };
29889};
29890
29891}
29892ABSL_NAMESPACE_END
29893}"###;
29894        let expanded = source
29895            .replace("ABSL_NAMESPACE_BEGIN ", "")
29896            .replace("ABSL_NAMESPACE_END", "")
29897            .replace(
29898                ",\n            REQUIRES(\"U must be convertible to int\", std::is_convertible<U, int>)>",
29899                ">",
29900            );
29901        assert!(
29902            crate::grammar_test_support::tree(source)
29903                .root_node()
29904                .has_error()
29905        );
29906        assert!(
29907            !crate::grammar_test_support::tree(&expanded)
29908                .root_node()
29909                .has_error()
29910        );
29911        let actual = parse_cpp_declarations(source, "expansion.h");
29912        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
29913        assert_eq!(
29914            declaration_expansion_inventory(&actual),
29915            declaration_expansion_inventory(&explicit)
29916        );
29917        for (alias, text) in [
29918            ("value_type", "using value_type = int;"),
29919            ("rebind", "using rebind = Base<T>;"),
29920            ("key_type", "using key_type = value_type;"),
29921            ("slot_type", "using slot_type = long;"),
29922        ] {
29923            let fq_name = format!("absl::container_internal.raw_hash_set${alias}");
29924            // Kind, owner, target, template head, guard, and lexical scope all
29925            // agree with the explicitly written class.
29926            assert_eq!(
29927                sentinel_alias_facts(&actual, &fq_name),
29928                sentinel_alias_facts(&explicit, &fq_name),
29929                "{alias}"
29930            );
29931            // The range is the alias's own source text, line included.
29932            let unit = actual
29933                .declarations()
29934                .iter()
29935                .find(|unit| unit.fq_name() == fq_name)
29936                .unwrap();
29937            let start = source.find(text).unwrap();
29938            assert_eq!(
29939                actual.declaration_ranges(unit),
29940                [Range {
29941                    start_byte: start,
29942                    end_byte: start + text.len(),
29943                    start_line: source[..start].matches('\n').count() + 1,
29944                    end_line: source[..start].matches('\n').count() + 1,
29945                }],
29946                "{alias}"
29947            );
29948        }
29949        let rendered =
29950            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$key_type");
29951        assert!(
29952            rendered.contains(r#"lexical_scope: ["absl", "container_internal", "raw_hash_set"]"#)
29953                && rendered
29954                    .contains(r#"["absl", "container_internal", "raw_hash_set", "key_type"]"#),
29955            "the target resolves in the recovered class: {rendered}"
29956        );
29957        assert!(
29958            sentinel_alias_facts(&actual, "absl::container_internal.raw_hash_set$slot_type")
29959                .contains(r#"Defined("ABSL_HAVE_SLOT")"#),
29960            "the guarded alias keeps its guard"
29961        );
29962    }
29963
29964    #[test]
29965    #[hardwon("#6902: a fragmented sentinel class published its type alias as a field")]
29966    fn unproven_sentinel_class_does_not_own_or_field_its_alias() {
29967        // Without the class's displaced `};` the sentinel recovery cannot
29968        // prove where raw_hash_set ends, so it recovers no class at all.
29969        let source = r###"namespace absl {
29970ABSL_NAMESPACE_BEGIN namespace container_internal {
29971
29972class raw_hash_set : public Base {
29973 public:
29974  using value_type = int;
29975
29976  template <class U,
29977            REQUIRES("U must be convertible to int", std::is_convertible<U, int>)>
29978  void insert(U value) { (void)value; }
29979
29980}
29981ABSL_NAMESPACE_END
29982}"###;
29983        let parsed = parse_cpp_declarations(source, "unproven.h");
29984        let declarations = parsed.declarations();
29985        assert!(
29986            declarations
29987                .iter()
29988                .all(|unit| !unit.fq_name().contains("raw_hash_set")),
29989            "no member may claim an owner the recovery did not prove: {declarations:#?}"
29990        );
29991        assert!(
29992            declarations
29993                .iter()
29994                .filter(|unit| unit.identifier() == "value_type")
29995                .all(|unit| unit.is_class() && parsed.type_aliases.contains(unit)),
29996            "the alias must not become a field: {declarations:#?}"
29997        );
29998    }
29999
30000    #[test]
30001    #[hardwon("#6902: consecutive decorated export classes lost most of their members")]
30002    fn recovered_consecutive_export_classes_agree_with_expansion() {
30003        let source =
30004            function_like_export_class_fixture("Invalid_Argument final", "Invalid_Argument");
30005        let tree = crate::grammar_test_support::tree(&source);
30006        for name in ["Exception", "Invalid_Argument"] {
30007            crate::grammar_test_support::assert_function_like_export_class_head(
30008                &tree, &source, name,
30009            );
30010        }
30011        let expanded = source.replace("PROJECT_PUBLIC_API(2, 0) ", "");
30012        let actual = parse_cpp_declarations(&source, "expansion.h");
30013        let explicit = parse_cpp_declarations(&expanded, "expansion.h");
30014        assert_eq!(
30015            declaration_expansion_inventory(&actual),
30016            declaration_expansion_inventory(&explicit)
30017        );
30018    }
30019
30020    #[test]
30021    #[hardwon(
30022        "5e79db920891: quicksilver round 4 survivor tests: standalone field-list macros lost their fields"
30023    )]
30024    fn standalone_field_list_agrees_with_expansion() {
30025        let source =
30026            "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { FIELDS\n};\n";
30027        let expanded = "#define FIELDS int first;\n#define FIELDS int first;\nstruct Handle { int first;\n};\n";
30028        let tree = crate::grammar_test_support::tree(source);
30029        assert!(
30030            crate::grammar_test_support::nodes(tree.root_node())
30031                .iter()
30032                .any(|node| node.kind() == "ERROR"
30033                    && node
30034                        .named_child(0)
30035                        .is_some_and(|child| child.kind() == "identifier")),
30036            "{}",
30037            tree.root_node().to_sexp()
30038        );
30039        let actual = parse_cpp_declarations(source, "expansion.h");
30040        let explicit = parse_cpp_declarations(expanded, "expansion.h");
30041        assert_eq!(
30042            declaration_expansion_inventory(&actual),
30043            declaration_expansion_inventory(&explicit)
30044        );
30045    }
30046
30047    #[test]
30048    #[hardwon(
30049        "5e79db920891: quicksilver round 4 survivor tests: recovered constructor parameters became fields"
30050    )]
30051    fn recovered_constructor_parameters_are_not_fields() {
30052        let source = r#"namespace Botan {
30053class EXPORT(2,0) Key final {
30054public:
30055    Key(int value);
30056    void run();
30057};
30058class EXPORT(2,0) DL_Group final {
30059public:
30060    ATTRIBUTE("use factory") explicit DL_Group(const char* name);
30061    DL_Group(int value);
30062    int get_p() const;
30063};
30064}"#;
30065        let actual = parse_cpp_declarations(source, "constructors.h");
30066        let expanded = source
30067            .replace("EXPORT(2,0) ", "")
30068            .replace("ATTRIBUTE(\"use factory\") ", "");
30069        let explicit = parse_cpp_declarations(&expanded, "constructors.h");
30070        assert_eq!(
30071            declaration_expansion_inventory(&actual),
30072            declaration_expansion_inventory(&explicit)
30073        );
30074    }
30075}